在 Java 中,如何确保布尔标志的安全、一致并发使用,同时最大限度地减少时间性能影响?更新争用条件原子布尔运算同步更新:基准测试
在我的场景中,我的对象基本上是原始数组包装器,它们在发生写入访问时设置布尔“脏”标志。DirtyArray
public class DirtyArray {
private byte[] data;
public DirtyArray(byte[] data) {
this.data = data;
}
private boolean dirty = false;
public void setValue(int index, byte value) {
dirty = true;
data[index] = value;
}
public boolean isDirty() {
return dirty;
}
}
肮脏的标志只会从 到 。false
true
我需要使此安全并发使用:有一个或多个线程可能会修改数组()。有一个或多个线程在 GCed 之前捕获,并且如果它已被修改,则应将其写入磁盘 ()。setValue
DirtyArray
isDirty
现在,如果我理解正确,像上面那样这样做是不安全的:实际上,从线程的角度来看,商店可以在商店之前重新排序。因此,查看并不能保证没有修改。isDirty
data[index]=value
dirty=true
isDirty()==false
data
这是正确的吗?
假设是的,那么制作标志应该可以解决这个问题。但是,在下面的基准测试中,我看到这样做时速度会降低约50x-100x。dirty
volatile
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void touchAll()
{
for (int i = 0; i < numEntities; i++)
bytes.setValue(i, ( byte ) 2);
}
使用AtomicBoolean和Java 9中引入的内存排序get/set变体,我有这个变体:
public class DirtyArray {
private byte[] data;
public DirtyArray(byte[] data) {
this.data = data;
}
private AtomicBoolean dirty = new AtomicBoolean();
public void setValue(int index, byte value) {
if (!dirty.getPlain())
dirty.setRelease(true);
data[index] = value;
}
public boolean isDirty() {
return dirty.getAcquire();
}
}
其性能(在上面的基准测试中)与原始非易失性版本相同。
这样做安全吗?也就是说,它是否保证何时修改我会看到?(在我的理解中,它应该是,但只是因为只有从到,永远不会回来。data
isDirty()==true
dirty
false
true
是否有其他变体来实现此保证,甚至可能允许重置为 ,理想情况下不会对性能产生负面影响?dirty
false
更新
我同意到目前为止对答案的一般评估,即保证已更改的数组和标志之间一致性的唯一方法是同步和。Pak Uula指出的竞争条件才是真正的问题,而不是让肮脏的旗帜可见。所以基本上我上面问的问题是错误的问题......data
dirty
setValue
isDirty
有关更多上下文:这是关于在 https://github.com/imglib 中存储透明缓存图像的像素。它用于非常紧密的循环中,从同步中接受打击并不是一个真正的选择。典型的使用场景是:
- 多个线程修改映像(由许多线程支持)。
DirtyArrays
- 检查发生在另一个线程上,该线程在垃圾回收之前捕获(在 的持有者上),如果它很脏,则将其写入磁盘。
isDirty()
DirtyArray
PhantomReference
DirtyArray
我现在的观点是,这应该比单个呼叫更粗糙。有一些“自然”的同步点会发生,因为线程通过从中获取它们(当然会掩盖细节)在s之间切换,线程在线程池中并从共享队列中获取作业,或者线程以其他方式等待彼此。在这些同步点上,早期(按程序顺序)的效果必须变得可见。因此,我倾向于只使用普通的不同步版本,并依靠较粗糙级别的同步。setValue()
DirtyArray
ConcurrentHashMap
setValue()
唯一让我有点头疼的是清理是由垃圾回收触发的,我必须确保(持有者)在粗略级别同步点之前没有被收集。但我认为我可以通过保持强大的引用并在必要时添加可访问性围栏来确保这一点。DirtyArray