在我的场景中,我有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标志之间的一致性的唯一方法是同步setValueisDirtyPak 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/

    10-09 07:11