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

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时.我通常会尝试确保只将不可变对象引用传递给将在另一个线程中使用它们的类,但有时这可能很困难.

解决方法:

这是一个很难回答的问题,不幸的是,答案是“它取决于”.在课程的线程安全方面,您有三种选择:

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

你基本上是在问你是否应该使用#2或#3.您担心另一个开发人员使用该类并且不知道它需要外部同步的情况.我喜欢使用JCIP annotations @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();
  }
}
上一篇:c – 有没有办法同步考虑崩溃的进程?


下一篇:java – synchronized和static synchronized有什么区别?