我最近正在阅读一些关于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;
}
}
最后一个类是线程安全的吗?
答案 0 :(得分:6)
简答:
是的,但在最后一种情况下你甚至不需要synchronized
。 setValue
唯一做的是单操作,写操作 - 并且每个操作中的挥发性都是原子的。也就是说,每次写入都是原子的,每次读取都是原子的。
更长的回答:
当然,如果您尝试使用如下模式增加值:
NotSure ns = new NotSure();
int v = ns.getValue();
ns.setValue(v + 1);
...那么不是线程安全的,因为它涉及ns.value
上的两个操作(读和写),而volatile
只给你原子性一次手术。在这种情况下,即使将synchronized
添加到两者 getter和setter也是不够的,因为可以在两个方法调用之间注入操作。
最后一点实际上是声称“你可以通过同步[对象的]数据来确保线程安全”的一个反驳论点。如果您想要一种增加NotSure.value
的线程安全方法,则需要同步整个增量操作,而不仅仅是访问对象的数据。在这种情况下,你将需要同步setter,因为它可以在增量方法的操作之间插入它自己。 getter仍然不需要同步,因为volatile
关键字将确保getter获得预递增或后递增的值。
答案 1 :(得分:0)
确实没有特别需要将方法getValue或setValue设置为同步。
我建议将变量值设为同步。
可以同时进行getter或setter调用。如果从两个单独的线程调用(一个进行getter调用,另一个进行setter调用。制作正被访问的对象或数据,我们可以使其成为线程安全的。