use*_*534 9 java multithreading
在Java中以原子方式分配引用意味着什么?
那么,如果引用赋值在Java中不是原子的,那会是什么错误?
Tag*_*eev 12
这意味着你永远不会得到损坏的引用.假设您有以下课程:
class MyClass {
Object obj = null;
}
Run Code Online (Sandbox Code Playgroud)
在内存中obj是一个空指针,通常它是一个整数0x00000000.然后假设在一个线程中你有一个任务:
this.obj = new Object();
Run Code Online (Sandbox Code Playgroud)
假设它new Object()在内存中分配并且具有类似的指针0x12345678.参考原子性确保当您obj从另一个线程检查时,您将具有空指针(0x00000000)或指向新对象的指针(0x12345678).但在任何情况下你都无法得到部分指定的参考(如0x12340000)无处指向.
这可能看起来很明显,但这种问题可能出现在低级语言中,例如C取决于CPU架构和内存对齐.例如,如果指针未对齐并穿过缓存行,则可能无法同步更新.为了避免这种情况,Java虚拟机始终对齐指针,因此它们永远不会跨越缓存行.
那么Java引用是非原子的,当解除引用从另一个线程写入的引用时,你有可能得到的不是在赋值之前或之后引用的对象,而是随机内存位置(可能导致分段错误,已损坏)堆或任何其他灾难).
CKi*_*ing 10
让我们考虑经典的双重检查锁定示例来理解为什么引用需要是原子的:
class Foo {
private Helper result;
public static Helper getHelper() {
if (result == null) {//1
synchronized(Foo.class) {//2
if (result == null) {//3
result = new Helper();//4
}
}
}
return result//5;
}
// other functions and members...
}
Run Code Online (Sandbox Code Playgroud)
让我们考虑两个将调用该getHelper方法的线程:
result是null.result是null上行号3Helper Helper第4行,但Thread-2执行第1行.步骤4和5是可能出现不一致的地方.在步骤4,有可能没有完全实例化对象,但是result变量已经将部分创建的Helper对象的地址标记在其中.如果Step-5在Helper对象完全初始化之前执行甚至纳秒,则Thread-2将看到该result引用不是,null并且可能返回对部分创建的对象的引用.
解决问题的方法是标记result为volatile或使用AtomicReference.话虽如此,上述场景在现实世界中不太可能发生,并且有更好的方法来实现,而Singleton不是使用双重检查锁定.
以下是使用以下方法实现双重检查锁定的示例AtomicReference:
private static AtomicReference instance = new AtomicReference();
public static AtomicReferenceSingleton getDefault() {
AtomicReferenceSingleton ars = instance.get();
if (ars == null) {
instance.compareAndSet(null,new AtomicReferenceSingleton());
ars = instance.get();
}
return ars;
}
Run Code Online (Sandbox Code Playgroud)
如果您有兴趣了解为什么第5步可能导致内存不一致,请查看此答案(如评论中的pwes所示)
我假设你在问AtomicReference<V>.
这个想法是,如果两个或多个线程读取或更新引用类型变量的值,您可能会得到意外的结果.例如,假设每个线程检查某个引用类型变量是否为null,如果它为null,则创建该类型的实例并更新该引用变量.
如果两个线程同时看到变量为null,则可能会导致创建两个实例.如果您的代码依赖于使用该变量引用的同一实例的所有线程,那么您将遇到麻烦.
现在,如果您使用AtomicReference<V>,可以使用该compareAndSet(V expect, V update)方法解决此问题.因此,只有当某个其他线程没有将其击败时,线程才会更新变量.
例如 :
static AtomicReference<MyClass> ref = new AtomicReference<> ();
...
// code of some thread
MyClass obj = ref.get();
if (obj == null) {
obj = new MyClass();
if (!ref.compareAndSet (null, obj)) // try to set the atomic reference to a new value
// only if it's still null
obj = ref.get(); // if some other thread managed to set it before the current thread,
// get the instance created by that other thread
}
Run Code Online (Sandbox Code Playgroud)