Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/337.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何从不同的线程将条目填充到映射中,然后从单个后台线程迭代映射并发送?_Java_Multithreading_Thread Safety_Guava_Multimap - Fatal编程技术网

Java 如何从不同的线程将条目填充到映射中,然后从单个后台线程迭代映射并发送?

Java 如何从不同的线程将条目填充到映射中,然后从单个后台线程迭代映射并发送?,java,multithreading,thread-safety,guava,multimap,Java,Multithreading,Thread Safety,Guava,Multimap,我有一个below类,其中有一个add方法,另一个线程调用该方法来填充我的clientdtotimestampHoldermultimap。然后在下面的同一个类中,我启动一个后台线程,它每60秒运行一次,并调用一个processData()方法,该方法迭代相同的映射并将所有这些数据发送到其他服务 public class Handler { private final ScheduledExecutorService executorService = Executors.newSingle

我有一个below类,其中有一个
add
方法,另一个线程调用该方法来填充我的
clientdtotimestampHolder
multimap。然后在下面的同一个类中,我启动一个后台线程,它每60秒运行一次,并调用一个
processData()
方法,该方法迭代相同的映射并将所有这些数据发送到其他服务

public class Handler {
  private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
  private final Multimap<String, Long> clientidToTimestampHolder = ArrayListMultimap.create();

  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, 60, TimeUnit.SECONDS);
  }

  // called by another thread to populate clientidToTimestampHolder map
  public void add(final String clientid, final Long timestamp) {
    clientidToTimestampHolder.put(clientid, timestamp);
  }

  // called by background thread
  public void processData() {
    for (Entry<String, Collection<Long>> entry : clientidToTimestampHolder.asMap().entrySet()) {
      String clientid = entry.getKey();
      Collection<Long> timestamps = entry.getValue();
      for (long timestamp : timestamps) {
        boolean isUpdated = isUpdatedClient(clientid, timestamp);
        if (!isUpdated) {
          updateClient(String.valueOf(clientid));
        }
      }
    }
  }
}
使用包装器对多重映射进行线程安全引用(
ArrayListMultimap
是一个
ListMultimap
,即在列表中存储值):

不遵循此建议可能会导致不确定性行为

在您的情况下,您必须手动同步entries视图的迭代,因为其迭代器未同步:

public void processData() {
  synchronized (clientidToTimestampHolder) {
    for (Map.Entry<String, Long> entry : clientidToTimestampHolder.entries()) {
      String clientid = entry.getKey();
      long timestamp = entry.getValue();
      boolean isUpdated = isUpdatedClient(clientid, timestamp);
      if (!isUpdated) {
        updateClient(String.valueOf(clientid));
      }
    }
    clientidToTimestampHolder.clear();
  }
}

您可以(应该?)为数据创建自己的值类来存储
String
long
字段,并使用它而不是通用的
映射。现在输入

,您的代码主要是观察映射不一致,因为在一次迭代中,您可能会有
[1:“value1”,2:“value2”,3:“value3”]
在地图中,下一次迭代地图可能是
[1:“value1”,2:“value2”,3:“value3”,4:“value4”]
。主要的问题是,我认为MultiMap不能确保元素排队的顺序(请参阅),因此您可以在迭代过程中跳过一个元素(由您决定它是否危险)

如果您确实需要停止每个put操作,那么确实可以使用@Xaerxess方法在processData()内同步映射。您提到的另一种可能性是制作一些,基本上是在多重贴图的快照上进行迭代,首先您要做:

public Multimap<String, Long> getClientidToTimestampHolder(){
    return ImmutableSetMultimap.copyOf(clientidToTimestampHolder);
}

为什么需要同步?因为如果您想要在时间t获得精确的映射,那么您需要防止其他线程向其添加元素。这是通过Java实现的,因此,只要一个线程(这里是您的后台线程)在映射上获得了锁,其他线程就无法在您读取多映射时访问该多映射。

还有,在完成处理后如何从映射中删除条目?那么我是否需要创建
clientdtotimestampHolder
map的副本,然后将其传递给
processData
map?基本上,我想每60秒处理
clientidToTimestampHolder
map中的任何内容,并且
processData
方法每60秒仅由单个后台线程调用,那么为什么我们需要同步该方法?@user1234,因为另一个线程可以在迭代引导期间修改multimap(可能)到
ConcurrentModificationException
,您希望这里有确定性行为(文档中提到,不遵循此建议可能会导致非确定性行为)。好的,现在就知道了。。如果删除已处理的条目,则每隔60秒,它将再次开始处理旧条目,这又如何呢?正确的?基本上,我希望每隔60秒处理一次映射中的所有内容。迭代后,您可以在同步块内执行
clientdtotimestampHolder.clear()
。但如果这是您的用例,那么您根本不需要多重映射,请使用
BlockingQueue
。我将更新我的答案以显示我的意思。我不能跳过任何元素,因此我需要确保我正在迭代所有元素,一旦我完成处理,它应该从映射中删除,而不是永远保留在那里。那么你认为什么是最好的方法呢?删除我要添加代码的地方?它会出现在processData方法中吗?在这种情况下,我将使用@Xaerxess的答案并对迭代的集合进行同步,然后在同一个同步块中,放入remove方法(因此在processData中是)。因为只要不需要快照就可以创建防御副本,如果在迭代过程中需要停止添加元素,那么这是不够的,需要锁定对映射的所有访问。在我的问题中添加了更新。那就是你的意思?那正是我的意思!您更新了
processData
是错误的,因为它在每次
hasNext()
检查时调用了
迭代器#next()
两次。是的,收到了。因此,我可以
清除
同步块中的映射,同时按照您的建议使用for循环进行迭代,而不是在迭代器上使用remove;long timestamp=e.getValue();//等
。或者在编辑后按照我在帖子中的建议使用
BlockingQueue
// ...  
public void processData() {
  synchronized (clientidToTimestampHolder) {
    for (Map.Entry<String, Long> entry : clientidToTimestampHolder.entries()) {
      String clientid = entry.getKey();
      long timestamp = entry.getValue();
      boolean isUpdated = isUpdatedClient(clientid, timestamp);
      if (!isUpdated) {
        updateClient(String.valueOf(clientid));
      }
    }
    clientidToTimestampHolder.clear();
  }
}
private final LinkedBlockingQueue<Map.Entry<String, Long>> clientidToTimestampHolder =
    new LinkedBlockingQueue<>();

public void add(final String clientid, final Long timestamp) {
  clientidToTimestampHolder.offer(Maps.immutableEntry(clientid, timestamp));
}

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));
    }
  }
}
public Multimap<String, Long> getClientidToTimestampHolder(){
    return ImmutableSetMultimap.copyOf(clientidToTimestampHolder);
}
 public void processData() {
    Multimap<String, Long> tmpClientToTimestampHolder = getClientidToTimestampHolder();
    for (Entry<String, Collection<Long>> entry : tmpClientToTimestampHolder.asMap().entrySet()) {
      String clientid = entry.getKey();
      Collection<Long> timestamps = entry.getValue();
      for (long timestamp : timestamps) {
        boolean isUpdated = isUpdatedClient(clientid, timestamp);
        if (!isUpdated) {
          updateClient(String.valueOf(clientid));
        }
      }
    }
  }
synchronized (clientidToTimestampHolder){
            clientidToTimestampHolder.remove(key, value);//fill key,value, or use removAll(key)
}