这是课程:
@NotThreadSafe
public class MutableInteger {
private int value;
public int get() { return value;}
public void set(int value) { this.value = value;}
}
这是我想出的后置条件:get()
返回的值等于set()
设置的值或0。
很容易看出上述后置条件并不总是成立。以两个线程A和B为例。假设A将value
设置为5,然后B将其设置为8。在线程A中执行get()
会返回8。应该返回5。这是一种简单的竞争情况。
如何使此类线程安全?在 Java:并发性实践一书中,作者保护了value
并将这两种方法都放在同一个对象上。我不明白这对比赛条件有什么帮助。首先,set()
不是复合动作。那么,为什么我们需要同步它?即使在那之后,竞赛条件也不会消失。一旦线程从set()
方法退出,一旦释放了锁,另一个线程就可以获取该锁并设置一个新值。在初始线程中执行get()
将返回新值,这违反了后置条件。
(据我了解,作者正在保护get()
)以获取可见性。但是我不确定它如何消除比赛条件。
答案 0 :(得分:1)
首先,set()不是复合动作。那么,为什么我们需要同步它?
您并不是自己同步set()
,而是针对同一个对象同步get()
和set()
方法(假设您使这两个方法都同步了)。 )
如果您没有这样做,并且value
变量未标记为易失性,那么您将无法保证线程会看到正确的值,因为-线程缓存。 (线程a
可以将其更新为5,然后即使线程b
更新了线程a
仍可能看到8 。这就是缺少在这种情况下线程安全。)
您是正确的,所有引用分配都是原子的,因此在这种情况下不必担心引用损坏。
即使在那之后,竞赛条件也不会消失。一旦线程从set()方法退出,一旦释放锁,另一个线程就可以获取该锁并设置新值。
就线程安全而言,设置新值的新线程(或设置新值的新代码)根本不是问题,这是设计和预期的。问题在于结果是否不一致,或者特别是如果多个线程能够以不一致的状态并发查看对象。