从多个线程调用方法来填充数据结构,然后从单个后台线程读取该数据结构?

时间:2017-04-02 15:30:29

标签: java multithreading thread-safety atomic

我有一个类,其中多个线程调用我的add方法来填充clientidToTimestampHolder LinkedBlockingQueue。然后在同一个下面的类中,我有一个后台线程,它每30毫秒运行一次并调用processData()方法将clientidToTimestampHolder排到Map of Map,然后迭代该List以将数据发送到不同的服务通过调用适当的方法。

我可以使用不同的时间戳多次获得相同的用户ID,这就是我使用LinkedBlockingQueue和地图的原因。

public class Handler {
  private final ScheduledExecutorService executorService = Executors
      .newSingleThreadScheduledExecutor();
  private final LinkedBlockingQueue<Map.Entry<String, Long>> clientidToTimestampHolder =
      new LinkedBlockingQueue<>();

  private static class Holder {
    private static final Handler INSTANCE = new Handler();
  }

  public static Handler getInstance() {
    return Holder.INSTANCE;
  }

  private Handler() {
    executorService.scheduleAtFixedRate(new Runnable() {
      @Override
      public void run() {
        processData();
      }
    }, 0, 30, TimeUnit.MILLISECONDS);
  }

  // called by multiple threads to populate clientidToTimestampHolder map
  public void add(final String clientid, final Long timestamp) {
    clientidToTimestampHolder.offer(Maps.immutableEntry(clientid, timestamp));
  }

  // called by single background thread every 30 milliseconds
  public void processData() {
    final List<Map.Entry<String, Long>> entries = new ArrayList<>();
    clientidToTimestampHolder.drainTo(entries);
    for (Map.Entry<String, Long> entry : entries) {
      String clientid = entry.getKey();
      long timestamp = entry.getValue();
      boolean isUpdated = isUpdatedClient(clientid, timestamp);
      if (!isUpdated) {
        updateClient(String.valueOf(clientid));
      }
    }
  }
}

我的上述代码线程是否安全并且没有竞争条件,我不会错过processData()方法中的任何数据?由于add方法将从多个线程调用,然后我有一个后台线程,每30毫秒运行一次,调用processData()方法从clientidToTimestampHolder LinkedBlockingQueue中提取数据。

1 个答案:

答案 0 :(得分:1)

匿名类包含对外部类的this引用(即Handler)。在最终字段冻结之前,您已经不安全地发布了this引用,执行程序的线程在读取时可能看不到正确初始化的集合。

除此之外,我不知道processData将如何错过任何更新。如果排出操作没有捕获并发写入,则不会在下次运行时进行。除非你担心这个(在这种情况下,你需要锁定),只要你不修改集合,它对我来说就好了。

另一方面,String#valueOf对字符串的目的是什么?