CompletableFutures是否线程安全?

3

我有一个线程,它调用两个不同的线程。它将同一个CompletableFuture传递给这两个子线程。如果在这两个线程中同时调用了.get()方法,是否会出现任何并发问题?

  • 可能会破坏CompletableFuture吗?
  • 是否有可能看不到由.get()返回的对象上做出的最后更改?
  • 如果之后修改该对象会怎样?

作为具体示例,在下面的代码中,假设在cfInput完成后没有更改cfInput.get()返回的对象,那么这两个线程是否可能打印不同的值?

public void mainClass(CompletableFuture<ObjA> cfInput){
  class1.doAsync1(cfInput);
  class2.doAsync2(cfInput);
}

@Async
public void doAsync1(CompletableFuture<ObjA> cfInput){
  //logic
  System.out.println(cfInput.get().getObjB().getBlah());
  //logic
}

@Async
public void doAsync2(CompletableFuture<ObjA> cfInput){
  //logic
  System.out.println(cfInput.get().getObjB().getBlah());
  //logic
}

public class ObjA{
  private ObjB objB;
  public ObjB getObjB();
  public void setObjB();
}
public class ObjB{
  private String blah;
  public String getBlah();
  public void setBlah();
}

1
可能不是,这个说法太模糊了。.get()的合约并不真正需要任何并发问题。 - matt
1
同意@matt的观点。如果get()无法从多个线程中安全调用,对我来说似乎非常奇怪。 完全有理由认为,在某些多线程程序中,可能需要多个线程等待计算或查询的结果,如果您使用的是CompletableFuture,那么根据定义,您正在进行多线程编程。 - Solomon Slow
@SolomonSlow 谢谢你的信息。稍微改变一下问题,如果我从CompletableFuture获取对象并从中获取值,会引起问题吗?或者只有在某种程度上修改对象时才会引起问题? - Brian
1
@Brian,你的意思是get()返回的对象吗?但那是_你的_对象,对吧?我的意思是,它是你的代码提供来完成future的任何对象。你的代码示例没有显示该对象来自哪里,甚至没有显示该对象的类型。因此,除非你展示更多的代码,否则只有你能说它是否线程安全。 - Solomon Slow
1
你现在实际上不再询问 CompletableFuture。你正在询问由 get 返回的对象,它是一个自定义对象,并将遵循您选择在其中实现的任何线程安全规则。一旦 get 返回,您就不再处于 CompletableFuture 领域了。 - Sotirios Delimanolis
显示剩余2条评论
1个回答

5

CompletableFuture 本质上是线程安全的

从这个类被设计用于多线程环境可以简单地假设它是线程安全的,然而,在java.util.concurrent包的描述中更明确地指定了这一点:

内存一致性属性

Java语言规范的第17章定义了内存操作(如共享变量的读写)之间的“happens-before”关系。一个线程写入的结果只有在该写操作发生在另一个线程的读取操作之前,“happens-before”关系才能保证结果对另一个线程可见。 [...] 所有在java.util.concurrent及其子包中的类的方法都扩展了这些保证到更高层次的同步。尤其是:

  • [...]
  • 异步计算所表示的操作在另一个线程调用Future.get()获取结果之后,“happens-before”此检索操作之后的操作。

因此,这意味着任何在线程完成一个Future之前执行的写入都将对调用该Future的任何其他线程可见(即“happened-before”)。

你的对象本质上不是线程安全的

... 也不受CompletableFuture的“保护”

虽然CompletableFuture本身是线程安全的,并提供了一些关于写入可见性的保证,但它并不使你的对象线程安全。

例如,如果你修改通过CompletableFuture.get()返回的对象,那么这些更改不保证对任何其他线程可见,直到你进入另一个“happens-before”关系。因此,您可能需要额外的同步机制来强制执行该对象的线程安全性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接