我最近在看一些关于java并发的书。关于线程安全,如果无法使类不可变,则始终可以通过同步其数据来确保线程安全。
下面的类显然不是线程安全的
public class NotThreadSafe {
private int value;
public void setValue(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
然后我可以同步写入,但它仍然不是线程安全的
public class StillNotThreadSafe {
private int value;
public synchronized void setValue(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
因为我不仅需要同步写入,还需要同步读取
public class ThreadSafe {
private int value;
public synchronized void setValue(int value) {
this.value = value;
}
public synchronized int getValue() {
return this.value;
}
}
现在的问题是,通过使用 volatile 我可以保证其他线程将看到更新后的值,所以这让我认为这个类应该是线程安全的
public class NotSure {
private volatile int value;
public synchronized void setValue(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
最后一个类是线程安全的吗??
最佳答案
简答:
是的,但在最后一种情况下您甚至不需要 synchronized
。 setValue
做的唯一一件事是一个单个 操作,一个写入——并且在每个操作中,volatiles 都是原子的。也就是说,每次写入都是原子的,每次读取也是原子的。
更长的答案:
当然,如果您尝试使用如下模式增加值:
NotSure ns = new NotSure();
int v = ns.getValue();
ns.setValue(v + 1);
...那么这不是线程安全的,因为它涉及对 ns.value
的两个操作(读取和写入),而 volatile
只为您提供一个操作的原子性。在这种情况下,即使将 synchronized
添加到 getter 和 setter 也是不够的,因为可以在两个方法调用之间注入(inject)操作。
最后一点实际上是对“您始终可以通过同步 [对象的] 数据来确保线程安全”这一说法的反驳。如果您想要一种线程安全的方法来增加 NotSure.value
,您需要同步整个增量操作,而不仅仅是访问对象的数据。在这种情况下,您将需要同步 setter,否则它可能会在增量方法的操作之间插入自身。 getter 仍然不需要同步,因为 volatile
关键字将确保 getter 获得增量前或增量后的值。
关于java - 同步的、易变的和线程安全的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19827180/