8

假设我有以下课程将被大量阅读,但只是偶尔写入。它将在多线程 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 需要在读取繁重的环境中可变,是否有任何替代方案?谢谢你。

4

3 回答 3

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();
 }
于 2013-03-05T01:43:59.793 回答
2

如果您所做的只是设置 foo,那么您不需要同步该方法。使参考 volatile 就足够了。

于 2013-03-05T01:44:08.373 回答
2

在您说的链接中,有“不经常更新”使用的代码:

@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方法仅使用同步,因为它所做的不仅仅是设置value描述中所述的值,如果您所做的this.foo = in;只是原子。在本文中,“这种模式的脆弱性”意味着当您混合使用 volatile 和其他同步方法来做的不仅仅是简单的示例时,事情会很快变得混乱。有关ConditionLock接口以及ReentrantLock类,请参见包 java.util.concurrent.locks 。我认为,使用同步是作者所说的“更强的选择”。如果您还不知道,您还应该看到Object.waitObject.notifyObject.notifyAll 。

于 2013-03-05T09:53:01.397 回答