出于教育目的,我正在编写一个简单的版本AtomicLong,其内部变量受到保护ReentrantReadWriteLock.这是一个简化的例子:
public class PlainSimpleAtomicLong {
private long value;
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
public PlainSimpleAtomicLong(long initialValue) {
this.value = initialValue;
}
public long get() {
long result;
rwLock.readLock().lock();
result = value;
rwLock.readLock().unlock();
return result;
}
// incrementAndGet, decrementAndGet, etc. are guarded by rwLock.writeLock()
}
Run Code Online (Sandbox Code Playgroud)
我的问题:由于"value"是非易失性的,其他线程是否有可能通过不正确的初始值来观察PlainSimpleAtomicLong.get()?例如,线程T1创建L = new PlainSimpleAtomicLong(42)并与线程共享引用T2.为T2保证遵守L.get()为42?
如果没有,包装this.value = initialValue;到写锁定/解锁会有所作为吗?
第 17 章用发生在关系之前的方式解释并发代码。this.value = initialValue;在您的示例中,如果您采用两个随机线程,则和之间不存在发生之前关系result = value;。
所以如果你有类似的东西:
T1.start();T2.start();...L = new PlainSimpleAtomicLong(42);long value = L.get();您拥有的唯一发生前(hb) 关系(除了每个线程中的程序顺序)是:1 & 2 hb 3,4,5。
但4和5没有订购。然而,如果 T1L.get()在 T2 调用之前调用L.get()(从挂钟的角度来看),那么T1 和T2之间就会有hb关系。unlock()lock()
正如已经评论过的,我认为您提出的代码不会破坏 JVM/硬件的任何组合,但它可能会破坏 JMM 的理论实现。
至于您将构造函数包装在锁定/解锁中的建议,我认为这还不够,因为至少在理论上,T1 可以在运行构造函数主体之前释放对 L 的有效引用(非空)。因此,风险在于 T2 可能会在 T1 在构造函数中获取锁之前获取锁。再说一次,这种交错在当前的 JVM/硬件上可能是不可能的。
所以总而言之,如果你想要理论上的线程安全,我认为你不能没有volatile long value,这就是AtomicLong实现方式。volatile将保证在发布对象之前初始化该字段。最后请注意,我在这里提到的问题并不是由于您的对象不安全(请参阅@BrettOkken 答案),而是基于对象未跨线程安全发布的场景。
| 归档时间: |
|
| 查看次数: |
399 次 |
| 最近记录: |