如何处理对存储在Map中的POJO的并发更新和读取

时间:2017-11-08 04:29:53

标签: java multithreading concurrency java.util.concurrent concurrenthashmap

我计划使用最多2个并发线程运行的CHM实现一个简单的内存缓存进行查找。一个线程使用迭代器迭代和更新CHM,第二个线程从地图读取值。

正如我的理解和迄今为止我所经历的那样,CHM Iterator是故障安全的,这意味着迭代将发生在数据快照上。

因此我们假设线程A使用密钥从CHM中提取一个POJO值,它正在迭代/更新POJO。同时,ThreadB可以获得相同的POJO。那么此时的预期行为是什么?

  • ThreadB会看到ThreadA正在进行的更新吗?我想不是因为ThreadA仍在进行更新。
  • 如果是,请分享您的想法将如何发生?
  • 如果没有,请提出有效的替代方法,如果您已实施任何方式。

POJO如下所示。

Class Pojo{
    private volatile long a;
    private volatile long b;
    ....    

    public long getA() {
        return this.a;
    }

    void setA(long a) {
        this.a = a;
    }
}

3 个答案:

答案 0 :(得分:2)

  

正如我的理解和迄今为止我所经历的那样,CHM Iterator是安全故障的,这意味着迭代会发生在数据快照上。

这不正确。每the documentation

  

类似地,Iterators,Spliterators和Enumerations在或从创建迭代器/枚举时返回反映哈希表状态的元素。

[强调我的]

  

因此我们假设线程A使用密钥从CHM中提取一个POJO值,它正在迭代/更新POJO。同时,ThreadB可以获得相同的POJO。那么此时的预期行为是什么呢?

如果线程A正在改变POJO而线程B正在检查相同的POJO,则ConcurrentHashMap根本不相关:两个线程如何 POJO并不重要POJO本身如何处理并发更新和读取。

你没有告诉过我们关于POJO类的任何信息,但是除非它经过精心设计以允许原子更新和读取,否则线程B有时会以不一致的状态查看POJO,或多或少都是这样的,一些线程A的读取但不是全部。

答案 1 :(得分:0)

你不能做这样的事。

Pojo myPojo = new Pojo();
myPojo.setA(10);
myPojo.setB(11);

// Atomic replace
CHM.replace(key, myPojo);

CHM替换是原子的,当替换正在进行时,没有其他线程会读取POJO的陈旧值。

答案 2 :(得分:0)

  • 您的CHM的值应为AtomicReference
  • 使Pojo类不可变,更新任何新值应该创建一个新的Pojo对象。
  • 实现一个接受构造函数中所有参数的构造函数。
  • 实现每个set方法,如下面给出的示例
public Pojo setA(int newA ){
    Pojo newPojo = new Pojo(newA, this.getB());
    return newPojo;
}
  • 更新CHM时,请按照以下步骤进行操作
AtomicReference pojoRef = CHM.get(Key);
Pojo oldPojo = pojoRef.get();
Pojo newPojo = oldPojo.setA(10);
while(!pojoRef.compareAndSet(oldPojo, newPojo) ){
    AtomicReference pojoRef = CHM.get(Key);
    Pojo oldPojo = pojoRef.get();
    Pojo newPojo = oldPojo.setA(10);
}