同步对易失性字段的写访问(廉价读写块)

时间:2013-03-05 01:36:36

标签: java multithreading synchronization thread-safety volatile

假设我有以下课程将被大量阅读,但只是偶尔写的。它将用于多线程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在读取繁重的环境中需要变量,还是其他任何选择?谢谢。

3 个答案:

答案 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