Java volatile变量问题

时间:2010-02-03 17:37:29

标签: java multithreading concurrency

阅读关于Java并发的DZone article我想知道是否有以下代码:


    private volatile List list;
    private final Lock lock = new ReentrantLock();

    public void update(List newList) {
        ImmutableList l = new ImmutableList().addAll(newList);
        lock.lock();
        list = l;
        lock.unlock();
    }

    public List get() {
        return list;
    }

相当于:


    private volatile List list;

    public void update(List newList) {
        ImmutableList l = new ImmutableList().addAll(newList); 
        list = l;
    }

    public List get() {
        return list;
    }

为简洁起见,省略了try {} finally {}块。我假设ImmutableList类是一个真正不可变的数据结构,它拥有自己的数据,例如google-collections库中提供的数据。由于列表变量是易变的,基本上正在进行的是即时复制,只是跳过使用锁是不安全的?

6 个答案:

答案 0 :(得分:6)

在这个非常具体的例子中,我认为你可以没有锁定变量重新分配。

一般来说,我认为你最好使用AtomicReference而不是volatile变量,因为内存一致性效果是相同的,意图更清晰。

答案 1 :(得分:4)

是的,这两个代码示例在并发环境中的行为方式相同。易失性字段为never cached thread-locally,因此在一个线程调用update()后,用一个新列表替换列表,然后所有其他线程上的get()将返回新列表。

但是如果你有像这样使用它的代码:

list = get()
list = list.add(something) // returns a new immutable list with the new content
update(list)

然后它将无法按预期在任何一个代码示例上工作(如果两个线程并行执行,那么其中一个线程所做的更改可能会被另一个线程覆盖)。但是如果只有一个线程正在更新列表,或者新值不依赖于旧值,则没问题。

答案 2 :(得分:1)

重新阅读之后,这些是等效的。

答案 3 :(得分:1)

如果我们谈论时间和内存可见性。易失性读取非常接近正常读取所需的时间。所以,如果你正在做get()很多,那么差别不大。执行易失性写入所花费的时间大约是获取和释放锁定的1/3。所以你的第二个建议要快一点。

大多数人建议的内存可见性是等效的,即锁定获取发生之前的任何读取在锁定获取之后的任何写入之前,类似于在任何后续写入之前发生易失性读取之前的任何读取

答案 4 :(得分:1)

为了提供所需的线程安全性,必须满足以下标准:

  1. 写入变量不依赖于其当前值。
  2. 该变量不参与其他变量的不变量。
  3. 因为这里都满足 - 代码是线程安全的

答案 5 :(得分:0)

我认为volatile的默认同步行为并不能保证ReentrantLock行为,因此它可能对性能有所帮助。否则,我认为没关系。

相关问题