Java 如何以原子方式插入和更新CHM中特定密钥的队列?

Java 如何以原子方式插入和更新CHM中特定密钥的队列?,java,multithreading,thread-safety,Java,Multithreading,Thread Safety,我正在使用Java7。在一个由多个线程填充的类中,我有一个ConcurrentHashMap围绕AtomicReference,如下所示: private final AtomicReference<ConcurrentHashMap<Channel, ConcurrentLinkedQueue<Holder>>> channelByHolderReference = new AtomicReference<>(new Conc

我正在使用Java7。在一个由多个线程填充的类中,我有一个
ConcurrentHashMap
围绕
AtomicReference
,如下所示:

  private final AtomicReference<ConcurrentHashMap<Channel, ConcurrentLinkedQueue<Holder>>> channelByHolderReference =
        new AtomicReference<>(new ConcurrentHashMap<Channel, ConcurrentLinkedQueue<Holder>>());

 // below method will be called by multiple threads to populate and update the map
 // is below method thread safe and I won't loose any update?
  public void add(final Channel channel, final Holder holder) {
    ConcurrentMap<Channel, ConcurrentLinkedQueue<Holder>> holderByChannel =
        channelByHolderReference.get();
    ConcurrentLinkedQueue<Holder> messageHolder = holderByChannel.get(channel);
    if (messageHolder == null) {
      messageHolder = Queues.newConcurrentLinkedQueue();
      ConcurrentLinkedQueue<Holder> currentMessageHolder =
          holderByChannel.putIfAbsent(channel, messageHolder);
      if (currentMessageHolder != null)
        messageHolder = currentMessageHolder;
    }
    messageHolder.add(holder);
  }

  // called only by single background thread every 30 seconds
  public void send() {
    ConcurrentHashMap<Channel, ConcurrentLinkedQueue<Holder>> holderByChannel =
        channelByHolderReference
            .getAndSet(new ConcurrentHashMap<Channel, ConcurrentLinkedQueue<Holder>>());
    for (Entry<Channel, ConcurrentLinkedQueue<Holder>> entry : holderByChannel
        .entrySet()) {
            // now iterate the map and do some processing
        }
  }   
私有最终原子引用channelByHolderReference=
新的原子引用(新的ConcurrentHashMap());
//多个线程将调用下面的方法来填充和更新映射
//下面的方法是线程安全的,我不会丢失任何更新吗?
公共无效添加(最终通道,最终持有人){
ConcurrentMap holderByChannel=
channelByHolderReference.get();
ConcurrentLinkedQueue messageHolder=holderByChannel.get(通道);
if(messageHolder==null){
messageHolder=Queues.newConcurrentLinkedQueue();
ConcurrentLinkedQueue currentMessageHolder=
holderByChannel.putIfAbsent(通道,消息持有者);
if(currentMessageHolder!=null)
messageHolder=currentMessageHolder;
}
messageHolder.add(holder);
}
//每30秒仅由单个后台线程调用
公共无效发送(){
ConcurrentHashMapHolderByChannel=
channelByHolderReference
.getAndSet(新的ConcurrentHashMap());
对于(条目:holderByChannel
.entrySet()){
//现在迭代映射并进行一些处理
}
}   
对于每个
频道
我将有多个
持有者
对象,这就是为什么每个频道都有
持有者
对象的
队列
。这里的
Channel
是我代码中的一个枚举

我上面的
add
方法将由多个线程调用,然后我在同一个类中有另一个
send
方法,该方法仅由单个后台线程每隔几秒钟调用一次。因此,无论我在该映射中有什么,我都会从一个后台线程每隔30秒发送一次(通过调用send方法)


我的问题是-我的
add
方法在填充和更新每个通道的多个
holder
对象的映射时是否线程安全?我是否需要
AtomicReference
,因为据我所知,CHM已经同步了,所以我可能不需要它?

请查看使用该设计可能会丢失消息。@Holger在代码的哪一部分,我会丢失消息?你能解释一下吗?我做错什么了吗?我认为这是Java7中的双重检查锁定方法,因为我现在无法使用
computeiFabSend
。在
send()
中,您正在用新映射替换映射。此时,可能有一个线程(甚至多个线程)仍在使用
add
中的旧映射,并且无法保证
send()
中的循环运行得足够慢,无法查看所有这些更新。当
send()
退出时,可能仍然有线程没有在旧映射上完成
add
操作。hmmm我明白你的意思了。。所以,我应该在
getAndSet
行之后睡一段时间(我知道这不是一个好主意),还是有更好的方法不丢失任何数据?