如何高效同步很少变化的数据?

How to efficiently synchronize rarely changed data?

我有一个 class 成员,它经常从一个线程读取但很少从另一个线程更新。如何有效地同步对它的访问?我想,如果我只是同步每个读取和写入,那么大多数读取同步将被浪费,因为对象不会在它们之间更改。

包括代码示例,虽然它很简单:

public class A {

    public static class B {
    }

    private List<B> bs =
        new ArrayList<>();

    public void addB(B b) {
        synchronized (this) {
            bs.add(b);
        }
    }

    public void run() {
        while (true) {
            List<B> bs;
            synchronized (this) {
                bs = new ArrayList<>(this.bs);
            }
            bs.forEach(b -> { /* do something */ });
        }
    }
}

假设 addBrun 运行 来自不同的线程。

ReadWriteLock

您可以使用 ReadWriteLock:

ReadWriteLock lock=new ReentrantReadWriteLock();

//Read access:
try{
    lock.readLock().lock();
    //Read access here
}finally{
    lock.readLock().unlock()
}

//Write access
try{
    lock.writeLock().lock();
    //Write access here
}finally{
    lock.writeLock().unlock()

这应该最大限度地减少 reader 的开销,因为只读时没有任何阻塞。

synchronized

另一种可能性是内在同步:

synchronized(LOCK){
    //Read or write access here
}

如果大部分工作在单个线程中完成并且只有一个 reader 线程(如您的场景),这将是最高效的解决方案。

使用线程安全类

第三种选择可能是使用线程安全数据classes/data 结构。这不需要您自己进行任何同步。在您的情况下,CopyOnWriteArrayList 将是一个不错的选择。它针对许多读取和罕见的写入访问进行了优化。

public class A {

    public static class B {
    }

    private List<B> bs =
        new CopyOnWriteArrayList<>();//important

    public void addB(B b) {
        //No synchronized needed
        bs.add(b);
    }

    public void run() {
        while (true) {
            //No synchronized block needed, just iterate over bs as it is thread safe
            bs.forEach(b -> { /* do something */ });
        }
    }
}

无论你选择什么,如果你关心它,你应该衡量性能影响。

要衡量性能,您可以使用 JMH 等工具。

您可以使用并发包中的 ReentrantReadWriteLock。