在Java中存在AtomicReference类。这是否意味着设置引用本身并不是原子操作?
例如,这是不是线程安全的(假设返回的值不能被修改)?:public void someMethod()
{
this.someList = Collections.unmodifiableList(new LinkedList<Object>());
}
public List<Object> getReadOnlyList()
{
return someList;
}
在C#中怎么样?
答案 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,以及用户定义的类型,不保证是原子的。除了为此目的设计的库函数,不能保证原子读 - 修改 - 写,例如在递增或递减的情况下。“