我正在编写一些必须处理多个线程的客户端-服务器-应用程序。我有一些服务器,每隔几秒发送一次 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/