我有一个客户端 - 服务器样式设计。我所做的是创建一个名为RequestController
的类,它控制和监视作为线程对象发出的所有服务器请求。
这些请求线程在同步Map
中跟踪(使用Collections.synchronizedMap(new HashMap<Long, RequestThread>())
创建)。检查每个请求线程的每半秒,如果它仍处于活动状态,它会向其感兴趣的侦听器对象(请求线程的属性)发送进度消息(如果已完成或失败),它会通知感兴趣的侦听器并将其自身从synchronized {当Map
方法完成时,{1}}。
我用来跟踪每个请求的run()
对象是监视所有请求的RequestThread
对象的受保护内部类。因此它可以访问包含它的地图。
我在这里遇到问题。当从地图中删除“死线程”时,我得到并发修改异常。我对地图的所有访问都是同步的,所以我不明白我是如何得到例外的。
这是控制循环代码:
RequestController
在this.reqTimer = new Timer("Request Timer", true);
TimerTask reqTask = new TimerTask()
{
@Override
public void run()
{
synchronized(reqList)
{
for(RequestThread rt : reqList.values())
rt.updateProgress();
}
}
};
this.reqTimer.scheduleAtFixedRate(reqTask, 500L, 500L);
方法的最后,调用RequestThread.run()
方法将其自身从地图中移除,如下所示:
RequestController
如果 public void removeRequest(long id)
{
synchronized(this.reqList)
{
this.reqList.remove(id);
}
}
方法在RequestThread.run()
方法循环遍历调用TimerTask.run()
方法的请求映射时完成updateProgress()
,则不会阻止删除但允许更改映射并导致我的例外。两个不同的线程如何同时锁定同一个对象?删除是否应该被阻止,因为它在更新完成之前起源于另一个线程?
答案 0 :(得分:2)
我要把我的意见转回答案。
两个不同的线程如何同时锁定同一个对象?
他们不能。并发修改必须在其他地方。我怀疑你的updateProgress()
正在从列表中删除。这可能是发生并发修改异常的地方。
for (RequestThread rt : reqList.values()) {
// you can't make any changes to reqList inside of the loop
rt.updateProgress();
}
如果您需要updateProgress()
从列表中删除请求,则可以:
updateProgress()
,以便它可以调用iterator.remove()
。List<RequestThread>
传递给updateProgress()
并添加要删除的请求。然后,当您在for循环之外(但仍在同步块中)时,您可以从reqList
中删除删除列表中的项目。答案 1 :(得分:2)
您需要在列表中之后执行删除: -
synchronized (reqList) {
// Keep track of items to be removed.
List<RequestThread> remove = new LinkedList<RequestThread>();
for (RequestThread rt : reqList.values()) {
rt.updateProgress(remove);
}
// Remove them.
reqList.removeAll(remove);
}
或者您需要使用Iterator
并使用其remove
方法。