在我的场景中,我有DirtyArray
对象,这些对象基本上是原始数组包装器,它们在发生写访问时设置 boolean 值“dirty”标志。
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
)。有一个或多个线程可以在对
DirtyArray
进行GC之前捕获它,并且如果已对其进行了修改(isDirty
),则应将其写到磁盘上。现在,如果我理解正确的话,这样做是不安全的:
实际上,从
isDirty
线程的角度来看,可以在data[index]=value
存储之前对dirty=true
存储进行重新排序。因此,看到
isDirty()==false
并不能保证data
未被修改。这样对吗?
假设是,那么制作
dirty
标志volatile
应该可以解决此问题。但是,在下面的基准测试中,我发现这样做的速度会降低50到100倍。
@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();
}
}
(在上述基准测试中)具有与原始非 volatile 版本相同的性能。这样安全吗?也就是说,它可以保证在修改
data
时,我会看到isDirty()==true
吗?(以我的理解,应该是,但是只是因为
dirty
只从false
变为true
,再也没有回来。)是否有其他变体来实现此保证,甚至可能允许将
dirty
重置为false
,且理想情况下不会对性能产生负面影响?
更新
我同意到目前为止对答案的一般评估,以确保更改的
data
数组和dirty
标志之间的一致性的唯一方法是同步setValue
和isDirty
。 Pak Uula指出的比赛条件是真正的问题,与其说让肮脏的旗帜可见,不如说是真正的问题。所以基本上我上面问的问题是错误的问题...对于更多上下文:这是关于在https://github.com/imglib中存储透明缓存的图像的像素。它用在非常紧密的循环中,从同步中获得成功并不是一个好的选择。典型的使用场景是:
DirtyArrays
支持)。 isDirty()
检查发生在另一个捕获的线程上垃圾收集之前的
DirtyArray
(的
PhantomReference
的持有者),如果不干净,请将其写入磁盘。 我现在的观点是,应该比单个
DirtyArray
调用更粗略地处理此问题。之所以会发生某种“自然”的同步点,是因为线程通过从setValue()
获取它们(在整个过程中进行了详细介绍)在DirtyArray
中进行切换,线程在线程池中并从共享队列中获取作业,或者某些线程在某些线程中其他方式互相等待。在这些同步点上,较早(按程序顺序)ConcurrentHashMap
的效果必须变得可见。因此,我倾向于只使用普通的未同步版本,并在更粗糙的级别上依赖同步。唯一让我有些头疼的是清理是由垃圾回收触发的,而且我必须确保在粗级同步点之前没有收集
setValue()
(的持有者)。但是我认为我可以通过保留强有力的引用并在必要时添加reachability fences来确保这一点。 最佳答案
AtomicBoolean
(或任何其他原子族)不保证与其他变量同步。所以不行。该代码不保证在修改数据时,您将获得isDirty()== true。您唯一的保证是所有线程始终看到相同的isDirty()值。实际上,列出的任何选项都不能保证。
保证的唯一方法是对set方法中的整个代码块进行独占锁定:if语句和赋值。这可以通过synced关键字(在方法上或在代码块中)或使用java.util.concurrency
中的一种锁定机制来实现
关于java - 在Java中,如何确保 boolean 标志的安全并发并发使用,同时最大程度地减少时间性能影响?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/62437201/