Java中的AtomicReference - 在线程安全环境中设置引用所必需的?

时间:2010-03-05 03:05:45

标签: c# java atomicreference

在Java中存在AtomicReference类。这是否意味着设置引用本身并不是原子操作?

例如,这是不是线程安全的(假设返回的值不能被修改)?:

public void someMethod()
{
   this.someList = Collections.unmodifiableList(new LinkedList<Object>());
}

public List<Object> getReadOnlyList()
{
   return someList;
}

在C#中怎么样?

5 个答案:

答案 0 :(得分:3)

根据Java Language Specification, version 3.0, Section 17.7

  

写入   并且引用的读取始终是原子的,无论它们是否已实现   为32或64位值。

AtomicReference可以执行比较并设置为原子动作。

这不是线程安全的:

public boolean changeList(List<Object> oldValue, List<Object> newValue) { 
    if (this.someList == oldValue) {
        // someList could be changed by another thread after that compare,
        // and before this set
        this.someList = newValue;
        return true;
    }
    return false;
}

答案 1 :(得分:3)

java.util.concurrent.atomic有时被忽视的package description详细阐述了一些常见用途。

附录:同样,java.util.concurrent的{​​{3}}可以方便地总结package description中详述的几个要点。

另外,如果您的List是不可变的,并且可以对final进行引用,请考虑JLS §17的潜在好处。

答案 2 :(得分:3)

  

这是否意味着设置引用本身并不是原子操作?

设置引用变量是原子的,但原子操作不一定是线程安全的。让我解释一下。

Atomic意味着任何观察者(线程)都会看到变量的旧值或新值,而不是其他东西。这并不意味着所有观察者在查看变量时都会看到新值。 (正如@Tom指出的那样,引用变量的原子性没有说明它引用的对象的原子性属性。)

为了让所有观察者都能看到变量中的新值,需要进行一些同步。对于变量的更新,如果出现以下情况,则会发生:

  • 变量声明为volatile
  • 对变量的访问/更新由同一原始监视器锁同步。

包含在相关“AtomicXxx”类中的变量也是线程安全的,但如果你想避免使用锁,你通常会使用其中一个类。作为原子“比较和替换”。

但是,这仅适用于对象引用的线程安全性。如果对象的状态未正确同步,则线程很可能会看到对象属性的陈旧值等等。

答案 3 :(得分:1)

如果您不使用AtomicReference或volatile关键字,并且读取引用的线程不是写入它的线程,则无法保证读取线程将看到更新的值。

在多处理器环境中尤其如此。 volatile关键字和AtomicReference(在内部使用volatile进行基本的set / get操作)强制执行内存屏障和缓存刷新,确保更新的值在主内存中可见。

答案 4 :(得分:1)

就C#而言,我自己找到了答案。根据C#语言规范的第5.5节设置引用是Atomic操作。

“以下数据类型的读写是原子的:bool,char,byte,sbyte,short,ushort,uint,int,float和reference types。此外,读取和写入具有底层类型的枚举类型在上一个列表中也是原子的。其他类型的读写,包括long,ulong,double和decimal,以及用户定义的类型,不保证是原子的。除了为此目的设计的库函数,不能保证原子读 - 修改 - 写,例如在递增或递减的情况下。“