在Java中,如何确保布尔标志的安全一致的并发使用,同时最大程度地降低时间性能影响?

问题描述 投票:3回答:1

在我的情况下,我有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,理想情况下不会对性能产生负面影响?

java multithreading atomic volatile
1个回答
0
投票

AtomicBoolean(或原子族的任何其他成员)不保证与其他变量同步。所以不行。该代码不保证在修改数据时,您将获得isDirty()== true。您唯一的保证是所有线程总是看到相同的isDirty()值。实际上,列出的选项都不能保证。

保证的唯一方法是在set方法内的whole代码块上具有排他锁:if语句together及其赋值。这可以通过synced关键字(在方法上或在代码块中)或使用java.util.concurrency

中的一种锁定机制来实现
© www.soinside.com 2019 - 2024. All rights reserved.