java - 遍历并发集合时的线程安全

标签 java multithreading synchronization concurrenthashmap

我正在编写一些必须处理多个线程的客户端-服务器-应用程序。我有一些服务器,每隔几秒发送一次 Activity 数据包。这些服务器在 ConcurrentHashMap 中维护,其中包含它们的端点与最后一个 Activity 包到达相应服务器的时间配对。

现在我有一个线程,它必须“整理”所有在特定时间内未发送 Activity 数据包的服务器。

我想我不能就那样做,对吧?

for( IPEndPoint server : this.fileservers.keySet() )
{
    Long time = this.fileservers.get( server );

    //If server's time is updated here, I got a problem

    if( time > fileserverTimeout )
        this.fileservers.remove( server );
}

有没有一种方法可以在不为整个循环获取锁的情况下解决这个问题(然后我也必须在其他线程中遵守)?

最佳答案

此处可能没有问题,具体取决于您在 map 中存储的内容。你的代码对我来说看起来有点奇怪,因为你似乎保存了“服务器不活动的持续时间”。

我记录该数据的第一个想法是存储“服务器处于 Activity 状态的最新时间戳”。那么您的代码将如下所示:

package so3950354;

import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class ServerManager {

  private final ConcurrentMap<Server, Long> lastActive = new ConcurrentHashMap<Server, Long>();

  /** May be overridden by a special method for testing. */
  protected long now() {
    return System.currentTimeMillis();
  }

  public void markActive(Server server) {
    lastActive.put(server, Long.valueOf(now()));
  }

  public void removeInactive(long timeoutMillis) {
    final long now = now();

    Iterator<Map.Entry<Server, Long>> it = lastActive.entrySet().iterator();
    while (it.hasNext()) {
      final Map.Entry<Server, Long> entry = it.next();
      final long backThen = entry.getValue().longValue();
      /*
       * Even if some other code updates the timestamp of this server now,
       * the server had timed out at some point in time, so it may be
       * removed. It's bad luck, but impossible to avoid.
       */
      if (now - backThen >= timeoutMillis) {
        it.remove();
      }
    }
  }

  static class Server {

  }
}

如果您真的想避免在调用 removeInactive 期间没有代码调用 markActive,则没有办法绕过显式锁定。您可能想要的是:

  • 允许并发调用 markActive
  • markActive 期间,不允许调用 removeInactive
  • removeInactive 期间,不允许调用 markActive

这看起来像是 ReadWriteLock 的典型场景,其中 markActive 是“读”操作,removeInactive 是“写”操作.

关于java - 遍历并发集合时的线程安全,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3950354/

相关文章:

java - 可以禁止 GoogleMap 在点击时移动到标记吗?

multithreading - 从单个应用程序中的多个线程调用 dll 函数是否安全?

java - 同步客户端-服务器游戏状态

java - java.io.FileDescriptor.sync() 在 Linux 上 fsync 目录吗?

java - httpclient放置问题

java - 如何在 Gradle 中添加 "system"依赖项?

java - 如何将嵌套 SQL 转换为 HQL

c# - 使用 Task.Factory.StartNew 新创建的线程启动非常慢

c# - 在多线程控制台应用程序中进行Web爬网

java - Findbugs - 对 java.util.concurrent.ConcurrentHashMap 的调用序列可能不是原子的