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

Java 如何处理对存储在映射中的POJO的并发更新和读取,java,multithreading,concurrency,java.util.concurrent,concurrenthashmap,Java,Multithreading,Concurrency,Java.util.concurrent,Concurrenthashmap,我计划使用最多由2个并发线程操作的CHM实现一个用于查找的简单内存缓存。一个线程使用迭代器迭代和更新CHM,第二个线程从映射中读取值 根据我的理解,我到目前为止所经历的CHM迭代器是故障安全的,这意味着迭代将发生在数据的快照上 因此,假设线程A从CHM中提取一个值,该值是使用键的POJO,并且它正在迭代/更新POJO。同时,ThreadB在同一个POJO上运行。那么,此时的预期行为是什么呢 ThreadB会看到ThreadA正在进行的更新吗?我猜不是因为线程仍然在进行更新。 如果是,请分享您的

我计划使用最多由2个并发线程操作的CHM实现一个用于查找的简单内存缓存。一个线程使用迭代器迭代和更新CHM,第二个线程从映射中读取值

根据我的理解,我到目前为止所经历的CHM迭代器是故障安全的,这意味着迭代将发生在数据的快照上

因此,假设线程A从CHM中提取一个值,该值是使用键的POJO,并且它正在迭代/更新POJO。同时,ThreadB在同一个POJO上运行。那么,此时的预期行为是什么呢

  • ThreadB会看到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;
    }
}
根据我的理解,我到目前为止所经历的CHM迭代器是故障安全的,这意味着迭代将发生在数据的快照上

这是不对的。Per:

类似地,迭代器、拆分器和枚举返回元素,这些元素反映了在或在创建迭代器/枚举后某个点的哈希表状态

[我的重点]


因此,假设线程A从CHM中提取一个值,该值是使用键的POJO,并且它正在迭代/更新POJO。同时,ThreadB在同一个POJO上运行。那么,此时的预期行为是什么呢

如果线程A正在变异一个POJO,而线程B正在检查同一个POJO,那么ConcurrentHashMap一点也不相关:这两个线程如何获得POJO无关紧要,只有POJO本身如何处理并发更新和读取


您还没有告诉我们关于POJO类的任何信息,但除非它经过精心设计,允许原子更新和读取,否则它或多或少是一种假设,即线程B有时会在不一致的状态下查看POJO,其中包含一些线程a的读取,但不是所有的读取。

您不能这样做吗

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

// Atomic replace
CHM.replace(key, myPojo);
CHM replace是原子的,在进行替换时,其他线程不会读取POJO的过时值。

  • CHM的值应该是原子引用
  • 使Pojo类不可变,更新任何新值都会创建一个新的Pojo对象
  • 实现一个接受构造函数中所有参数的构造函数
  • 实现每个set方法,如下面给出的示例
  • 更新CHM时,请遵循以下步骤

这是为了学习还是为了生产?如果是用于生产,请使用经过战斗测试的东西:即使POJO类经过精心设计,允许原子更新/读取,但如果写入是在读取之后启动的,则仍有可能丢失更新。@CKK:我认为这是给定的--没有人希望读取将来进行,也不是为了写过去。这就是为什么OP需要重新考虑使用
ConcurrentHashMap
,因为仍然无法保证不会错过OP想要的更新。更好的方法是使用
BlockingQueue
,其中一个线程创建并将POJO写入队列,另一个线程读取POJO。通过这种方法,可以保证写操作不会丢失,并且您还可以获得并行化功能。@CKing:我不确定您是如何确定“OP想要的”是“保证更新不会丢失”;这完全不是我如何解释他/她想要“查找缓存”的愿望。“THeTrad能看到ththa正在做的更新吗?我想不是因为thela还在进行更新。”“如果是,请分享你的想法,这将如何发生”。。和。。“如果没有,请建议有效的替代方法,如果您已经实施了任何。”。对我来说,这些都清楚地表明OP希望找到解决这些问题的方法。特别是解决这些问题的替代解决方案的最后一点。这就是说,你是对的,OP需要缓存,所以阻塞队列没有意义。
public Pojo setA(int newA ){
    Pojo newPojo = new Pojo(newA, this.getB());
    return newPojo;
}
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);
}