Java - 在另一个线程中使用可变对象引用的首选设计?

2024-02-04

public class ObjectA {

   private void foo() {
       MutableObject mo = new MutableObject();
       Runnable objectB = new ObjectB(mo);
       new Thread(objectB).start();
   }

}

public class ObjectB implements Runnable {

    private MutableObject mo;

    public ObjectB(MutableObject mo) {
        this.mo = mo;
    }

    public void run() {
       //read some field from mo
    }
}

正如您从上面的代码示例中看到的,我将一个可变对象传递给一个实现 Runnable 的类,并将在另一个线程中使用该可变对象。这是危险的,因为 ObjectA.foo() 在启动新线程后仍然可以更改可变对象的状态。这里确保线程安全的首选方法是什么?将 MutableObject 传递给 ObjectB 时是否应该复制它?可变对象是否应该确保内部正确的同步?我以前多次遇到过这种情况,尤其是在尝试在许多 GUI 应用程序中使用 SwingWorker 时。我通常尝试确保仅将不可变对象引用传递给将在另一个线程中使用它们的类,但有时这可能很困难。


这是一个很难回答的问题,不幸的是,答案是“视情况而定”。当涉及到类的线程安全时,您有三种选择:

  1. 让它不可变,那么你就不用担心了。但这不是你要问的。
  2. 使其线程安全。也就是说,在类内部提供足够的并发控制,以便客户端代码不必担心并发线程修改对象。
  3. 使其成为非线程安全的,并强制客户端代码进行某种外部同步。

您本质上是在问是否应该使用#2 还是#3。您担心其他开发人员使用该类并且不知道它需要外部同步的情况。我喜欢使用JCIP注释 http://jcip.net/annotations/doc/net/jcip/annotations/package-summary.html @ThreadSafe @Immutable @NotThreadSafe作为记录并发意图的一种方式。这并不是万无一失的,因为开发人员仍然必须阅读文档,但如果团队中的每个人都理解这些注释并一致应用它们,它确实会让事情变得更清晰。

对于您的示例,如果您想让该类不是线程安全的,您可以使用AtomicReference使其清晰并提供同步。

public class ObjectA {

  private void foo() {
     MutableObject mo = new MutableObject();
     Runnable objectB = new ObjectB(new AtomicReference<>( mo ) );
     new Thread(objectB).start();
  }
}

public class ObjectB implements Runnable {

  private AtomicReference<MutableObject> mo;

  public ObjectB(AtomicReference<MutableObject> mo) {
    this.mo = mo;
  }

  public void run() {
   //read some field from mo
   mo.get().readSomeField();
  }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java - 在另一个线程中使用可变对象引用的首选设计? 的相关文章

随机推荐