Java线程安全性设置对象的引用

时间:2017-11-22 00:26:01

标签: java multithreading

我想知道以下类是否是线程安全的:

class Example {

private Thing thing;

public setThing(Thing thing) {
    this.thing = thing;
    }

public use() {
    thing.function();
    }
}

具体来说,如果一个线程通过Example :: use来调用setThing而另一个线程在Thing :: function中会发生什么?

例如:

Example example = new Example();
example.setThing(new Thing());

createThread(example);  // create first thread
createThread(example);  // create second thread
//Thread1
while(1) {
  example.use();
}
//Thread2
while(1) {
    sleep(3600000); //yes, i know to use a scheduled thread executor
    setThing(new Thing());
}

具体来说,我想知道,当use()执行时调用setThing时,它是否会成功继续旧对象,或者更新对象的引用会以某种方式导致问题。

2 个答案:

答案 0 :(得分:1)

如果方法是共享资源并且线程未同步,则它们将发生冲突并且可能发生多种情况,包括覆盖由另一个线程计算并存储在共享变量中的数据。

如果该方法只有局部变量,那么你可以使用mutliple线程的方法,而不必担心赛车。但是,通常非辅助类操作其方法中的成员变量,因此建议使方法同步,或者如果您确切知道问题可能发生的位置,则使用{{1}锁定(也称为同步)方法的子范围。锁/对象。

答案 1 :(得分:1)

关于特定级别的线程安全性的推理有两点:

  1. 线程之间共享状态的可见性。
  2. 安全(保留类不变量)当多个线程通过类方法使用类对象时。
  3. Example类的共享状态仅包含一个Thing对象。

    1. 从可见性角度来看,该类不是线程安全的。其他线程看不到一个线程setThing的结果,因此它们可以处理陈旧数据。 NPE也是可以接受的,因为在类初始化期间thing的初始值为null
    2. 如果没有源代码,通过Thing方法访问use类是否安全是不可能的。但是Example调用use方法而没有任何同步,所以它应该是,否则Example不是线程安全的。
    3. 因此Example不是线程安全的。要修复第1点,您可以将volatile添加到thing字段,如果您确实需要setter或将其标记为final并在构造函数中初始化。确保满足2的最简单方法是将use标记为synchronized。如果您使用setThing标记synchronized,则不再需要volatile。然而,还有许多其他复杂的技术可以满足第2点。This伟大的书籍更详细地描述了这里所写的所有内容。