简体   繁体   English

以线程安全的方式填充地图并将该地图从后台线程传递给另一个方法?

[英]Populate map in a thread safe way and pass that map to another method from a background thread?

I have a below class in which add method will be called by multiple threads to populate channelMessageHolder CHM in a thread safe way.我有一个下面的类,其中多个线程将调用add方法以线程安全的方式填充channelMessageHolder CHM。

In the same class, I have a backgrond thread which runs every 30 seconds and it calls send method by passing data from the channelMessageHolder .在同一个类中,我有一个每 30 秒运行一次的后台线程,它通过从channelMessageHolder传递数据来调用send方法。

public class Processor {
  private final ScheduledExecutorService executorService = Executors
      .newSingleThreadScheduledExecutor();
  private final AtomicReference<ConcurrentHashMap<Channel, ConcurrentLinkedQueue<Message>>> channelMessageHolder =
                new AtomicReference<>(new ConcurrentHashMap<Channel, ConcurrentLinkedQueue<Message>>());

  private Processor() {
    executorService.scheduleAtFixedRate(new Runnable() {
      @Override
      public void run() {
        send();
      }
    }, 0, 30, TimeUnit.SECONDS);
  }

  // this will be called by only single background thread
  private void send(ConcurrentHashMap<Channel, ConcurrentLinkedQueue<Message>> messageByChannels) {
    for(Entry<Channel, ConcurrentLinkedQueue<Message>> entry : messageByChannels.entrySet()) {
      Channel channel = entry.getKey();
      ConcurrentLinkedQueue<Message> messageHolder = entry.getValue();

      while (!messageHolder.isEmpty()) {
        Message message = messageHolder.poll();
        ....
        // process this and send to database
      }      
    }
  }

  // called by multiple threads
  public void add(final Channel channel, final Message message) {
    // populate channelMessageHolder in a thread safe way
  }
}

Question

As you can see channelMessageHolder is already present in my Processor class so do I need to explicitly pass data from this map every 30 seconds to send method?正如您所看到的, channelMessageHolder已经存在于我的Processor类中,所以我是否需要每 30 秒显式地将数据从此映射中传递给 send 方法? Or I can directly use it in my send method?或者我可以直接在我的发送方法中使用它?

Confusion is, if I directly use it in my send method, then it will be populated by multiple threads at the same time so that's why I am using getAndSet method of AtomicReference to pass it to send method.令人困惑的是,如果我直接在我的 send 方法中使用它,那么它将被多个线程同时填充,这就是为什么我使用 AtomicReference 的getAndSet方法将它传递给send方法。

Let me know if what I am doing is wrong and there is any better way to do that?让我知道我在做什么是错的,有没有更好的方法来做到这一点?

Or I can directly use it in my send method without passing anything或者我可以直接在我的发送方法中使用它而不传递任何东西

You should be able to directly use it in the send method by saying channelMessageHolder.getAndSet(new ConcurrentHashMap<Channel, ConcurrentLinkedQueue<Message>>()) at the beginning of the send method without any issues.您应该能够直接使用它在send说方法channelMessageHolder.getAndSet(new ConcurrentHashMap<Channel, ConcurrentLinkedQueue<Message>>())在开始时send的方法没有任何问题。

That said, Java 8 has added a new method called computeIfAbsent to the ConcurrentHashMap class which means that you don't really need that AtomicReference that you are using.也就是说, Java 8ConcurrentHashMap类添加了一个名为computeIfAbsent的新方法,这意味着您并不真正需要正在使用的AtomicReference

As you can see channelMessageHolder is already present in my Processor class so do I need to explicitly pass data from this map every 30 seconds to send method?正如您所看到的,channelMessageHolder 已经存在于我的 Processor 类中,所以我是否需要每 30 秒显式地将数据从此映射中传递给 send 方法? Or I can directly use it in my send method?或者我可以直接在我的发送方法中使用它?

You can certainly use it directly in the send() method and you don't need the AtomicReference wrapper since ConcurrentHashMap is already synchronized.您当然可以直接在send()方法中使用它,并且您不需要AtomicReference包装器,因为ConcurrentHashMap已经同步。 What you need to worry about is that your key and value objects in the map are being properly synchronized.您需要担心的是映射中的键和值对象是否正确同步。 I assume the Channel is immutable and the ConcurrentLinkedQueue is concurrent so you should be good.我假设Channel是不可变的,而ConcurrentLinkedQueue是并发的,所以你应该很好。

// no need for AtomicReference
private final ConcurrentHashMap<Channel, ConcurrentLinkedQueue<Message>> channelMessageHolder =
     new ConcurrentHashMap<Channel, ConcurrentLinkedQueue<Message>>();

The ConcurrentHashMap takes care of the synchronization for you so the producer threads can add items into it at the same time that your sender thread is sending items out without clashing. ConcurrentHashMap为您处理同步,因此生产者线程可以在您的发送者线程发送项目的同时向其中添加项目而不会发生冲突。 An AtomicReference is only needed if you were trying to share an unsynchronized class between multiple threads.仅当您尝试在多个线程之间共享未同步的类时才需要AtomicReference

Confusion is, if I directly use it in my send method, then it will be populated by multiple threads at the same time so that's why I am using getAndSet method of AtomicReference to pass it to send method.令人困惑的是,如果我直接在我的 send 方法中使用它,那么它将被多个线程同时填充,这就是为什么我使用 AtomicReference 的 getAndSet 方法将它传递给 send 方法。

Right but this ok.是的,但这没关系。 Multiple threads will be adding messagesto the ConcurrentLinkedQueue .多个线程将向ConcurrentLinkedQueue添加消息。 Every 30 seconds your background thread starts up, gets the Channel , dequeues and then sends the messages that are in the queue at that moment.每 30 秒您的后台线程启动一次,获取Channel ,出列然后发送当时队列中的消息。 The ConcurrentLinkedQueue protects against race conditions of the producer and consumer. ConcurrentLinkedQueue防止生产者和消费者的竞争条件。

The problem that you have in your code is that this isn't reentrant since it relies on multiple calls to the queue:您在代码中遇到的问题是这不是可重入的,因为它依赖于对队列的多次调用:

while (!messageHolder.isEmpty()) {
    Message message = messageHolder.poll();

It works in your case because there looks to be only one thread dequeue-ing but the following code is better:它适用于您的情况,因为看起来只有一个线程出队,但以下代码更好:

while (true) {
    // only one call to the concurrent queue
    Message message = messageHolder.poll();
    if (message == null) {
        break;
    }
    ...
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM