假设我有以下课程将被大量阅读,但只是偶尔写的。它将用于多线程Web应用程序,因此它需要是线程安全的:
public class Foo {
private volatile String foo;
public String getFoo() {
return foo;
}
public synchronized String setFoo(String in) {
this.foo = in;
}
}
Java Concurrency(http://www.ibm.com/developerworks/java/library/j-jtp06197/index.html)指出这是一种保护写访问的脆弱方法,同时改善了读访问。什么是这种模式的更强的替代品?或者,如果foo在读取繁重的环境中需要变量,还是其他任何选择?谢谢。
答案 0 :(得分:15)
Volatile提供对没有同步的字段的快速线程安全无锁访问
private volatile String foo;
public String getFoo() {
return foo;
}
public void setFoo(String in) {
this.foo = in;
}
volatile解决了3个问题1)内存可见性2)双字段和长字段的原子写入3)禁止指令重新排序。但是,如果您需要在一个字段上进行多次操作作为一个原子事务(例如增量),这还不够。此代码已损坏
private volatile int id;
public void incrementId() {
id++;
}
因为如果2个线程同时读取并递增并保存结果,那么第一个增量的结果将被第二个增量的结果覆盖。为了防止这种情况发生,我们需要使用同步
private int id;
public synchronized int nextId() {
return ++id;
}
或java.util.concurrent.atomic包
private AtomicInteger id = new AtomicInteger();
public void incrementId() {
return id.incrementAndGet();
}
答案 1 :(得分:2)
如果你要做的就是设置foo,那么你不需要同步方法。使参考不稳定就足够了。
答案 2 :(得分:2)
在你提到的link处,有“不常更新”用法的代码:
@ThreadSafe
public class CheesyCounter {
// Employs the cheap read-write lock trick
// All mutative operations MUST be done with the 'this' lock held
@GuardedBy("this") private volatile int value;
public int getValue() { return value; }
public synchronized int increment() {
return value++;
}
}
increment
方法仅使用synchronized,因为它不仅仅是设置描述中所述的value
的值,如果您所做的只是this.foo = in;
是原子的。
在文本中,“这种模式的脆弱性”意味着当你将volatile和其他同步方法混合起来而不仅仅是简单的例子时,事情会变得非常快。
有关接口 Condition 和 Lock 以及类 ReentrantLock ,请参阅包java.util.concurrent.locks。我认为,使用synchronized是作者所说的“更强的替代品”。如果您还不知道,还应该看到 Object.wait , Object.notify 和 Object.notifyAll 。