在我的情况下,我有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
。
我需要确保此安全性可同时使用:有一个或多个线程可以修改数组(setValue
)。[GC]之前,有一个或多个线程可以捕获DirtyArray
,如果已经对其进行了修改(isDirty
),则应该将其写到磁盘上。
现在,如果我理解正确,则像上面这样进行不安全:实际上,从isDirty
线程的角度来看,data[index]=value
存储区可以在dirty=true
存储区之前重新排序。因此,看到isDirty()==false
并不能保证data
未被修改。
这是正确的吗?
[假设是,然后设置dirty
标志volatile
应该解决此问题。但是,在以下基准测试中,这样做时会看到〜50x-100x的速度下降。
@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
,理想情况下不会对性能产生负面影响?
AtomicBoolean
(或原子族的任何其他成员)不保证与其他变量同步。所以不行。该代码不保证在修改数据时,您将获得isDirty()== true。您唯一的保证是所有线程总是看到相同的isDirty()值。实际上,列出的选项都不能保证。
保证的唯一方法是在set方法内的whole代码块上具有排他锁:if语句together及其赋值。这可以通过synced关键字(在方法上或在代码块中)或使用java.util.concurrency