数据如何 stored/accessed 并防止地图中的竞争条件,java

How data is stored/accessed and preventing race conditions in maps, java

我们有这样的案例。

class A{
 class foo{
    //Map with a lot of entries
    private HashMap<String,String> dataMap; 

    public updater(){
        // updates dataMap
        // takes several milliseconds
    }

    public someAction(){
        // needs to perform read on dataMap
        // several times, in a long process
        // which takes several milliseconds
    }
}

问题是,someAction和updater都是可以同时调用的,someAction是比较频繁的方法。如果调用更新器,它可以从 dataMap 中替换很多值。我们需要 readAction 的一致性。如果该方法以旧 dataMap 开始,则所有读取都应使用旧 dataMap。

class foo{
    //Map with a lot of entries
    private HashMap<String,String> dataMap; 


    public updater(){
        var updateDataMap = clone(dataMap); // some way to clone data from map
        // updates updateDataMap instead of dataMap
        // takes several milliseconds
        this.dataMap = updateDataMap;       
    }

    public someAction(){
        var readDataMap = dataMap;
        // reads from readDataMap instead of dataMap
        // several times, in a long process
        // which takes several milliseconds
    }
}

这样能保证一致性吗?我相信克隆方法将在内存中分配一个不同的区域,并且新的引用将从那里发生。是否会对性能产生影响?还有oldDataMap的内存用完后会不会释放?

如果这是正确的方法,还有其他有效的方法吗?

我相信您的方法会奏效,因为 updater() 的所有更改都将发生在(深)副本中,并且 someAction() 不可见,直到在一次操作中,参考已更新。

我理解你不关心someAction()看到的是不是最新版的地图内容,只要地图是一致的,也就是不观察而它正在更新中。在这种情况下,您的 someAction() 无法查看不完整的地图。

请注意,最多 1 个线程应该能够调用 updater() - 两个线程同时调用它意味着只有其中一个线程可以写入更新的地图。我建议进行以下更改:

// no synchronization needed at this level, but volatile is important
private volatile HashMap<String,String> dataMap = new HashMap<>;

// if two threads attempt to call this at once, one blocks until the other finishes
public synchronized updater() {
    var writeDataMap = clone(dataMap);  // a deep copy

    // update writeDataMap - guaranteed no other thread updating
    // ... long operation

    dataMap = writeDataMap;             // switch visible map with the updated one 
}

public someAction() {
    var readDataMap = dataMap;

    // process readDataMap - guaranteed not to change while being read
    // ... long operation
}

这里重要的关键字是 volatile,以确保其他线程在 updater() 完成其工作后立即访问更新的映射。使用synchronized只是简单的防止多个updater()线程相互干扰,多为防御性

如果您想避免复制,您可以使用 java.util.concurrent.locks.ReentrantReadWriteLock 来保护对您的地图的访问。在updater中使用写锁,在someAction中使用读锁。