爪哇 - 首选设计使用在另一个线程可变对象引用?(Java - Preferred design f

2019-09-22 04:06发布

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的副本将其传递给对象B的时候? 如若可变对象确保正确的同步内部? 我碰到过很多次之前,试图在许多GUI应用程序使用的SwingWorker时尤其如此。 我通常会尽量确保只有不可变对象的引用传递给将在另一个线程使用它们的一类,但有时这是很困难的。

Answer 1:

这是一个很难回答的问题,答案,不幸的是,“这取决于”。 你有三个选择,当涉及到您的类的线程安全:

  1. 让它不可改变的,那么你不必担心。 但是,这是不是你在问什么。
  2. 让它线程安全的。 也就是说,提供足够的并发控制内部的类,客户端代码不必担心并发线程修改的对象。
  3. 让它不是线程安全的,并迫使客户端代码有某种外部同步的。

你基本上问你是否应该使用#2或#3。 你担心在那里另一名开发人员使用类,不知道它需要外部同步的情况。 我喜欢用JCIP注释 @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();
  }
}


Answer 2:

我认为你是它过于复杂。 如果是作为例子(局部变量,其中没有提及保持),你应该相信,没有人会试图写它。 如果是更复杂( A.foo()有更多的LOC)如果可能的话,创建它只是传递到线程。

new Thread(new MutableObject()).start();

如果不是(由于初始化),在一个块中声明,以便它立刻失控的范围,即使是在一个单独的私有方法可能。

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


Answer 3:

复制的对象。 因为您通过复制到一个新的线程,你不会有任何奇怪的可见性问题。 Thread.start总是会发生新的线程进入它的run方法之前。 如果更改此代码的对象传递给现有的线程,则需要适当的同步。 我推荐的java.util.concurrent阻塞队列。



Answer 4:

不知道您的具体情况,这个问题将是难以准确回答的问题。 答案完全取决于什么MutableObject表示,许多其他的线程如何可以同时修改它,无论是否同时他们读它是阅读对象是否照顾其状态更改线程。

对于线程安全,内部同步所有的读取和写入MutableObject可证明是“最安全”的事情,但它是以牺牲性能为代价。 如果竞争是非常高的读取和写入,那么你的程序可能会遇到性能问题。 你可以牺牲对互斥一些保证获得更好的性能 - 那些牺牲是否值得的性能提升完全取决于你试图解决的特定问题。

您还可以玩一些游戏和你如何去“内部同步”你的MutableObject ,如果这是你最终做。 如果你还没有准备好,我建议你上的差异读了volatilesynchronized ,并了解每个可以被用于确保不同情况线程安全。



文章来源: Java - Preferred design for using a mutable object reference in another thread?