如何存储/访问数据并防止Map中的竞争条件,java

dwbf0jvd  于 2021-06-30  发布在  Java
关注(0)|答案(2)|浏览(423)

我们有这样的案子。

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是一个更频繁的方法。如果调用updater,它可以替换datamap中的许多值。我们需要行动的一致性。如果该方法以old datamap开始,那么所有读取都应该以old 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的内存在使用后会被释放吗?
如果这是正确的方法,有没有其他有效的方法来达到同样的效果?

qacovj5a

qacovj5a1#

我相信你的方法会奏效,因为 updater() 将出现在(深度)副本中,并且在 someAction() 直到在单个操作中更新引用。
我知道你不在乎 someAction() 查看Map内容的最新版本,只要Map是一致的,也就是说,它在更新过程中没有被观察到。在这种情况下,你没有办法 someAction() 看一张不完整的Map。
请注意,最多只能有一个线程能够调用 updater() -两个线程同时调用它意味着其中只有一个线程可以编写更新的Map。我建议进行以下更改:

// 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() 线程之间相互干扰,并且大多是防御性的。

fnvucqvd

fnvucqvd2#

如果你想避免复制,你可以使用 java.util.concurrent.locks.ReentrantReadWriteLock 以保护对Map的访问。使用中的写锁 updater 读取锁定 someAction .

相关问题