从同步列表中删除元素的正确方法

问题描述:

我有一个客户端 - 服务器样式的设计。我所做的是创建一个名为RequestController的类,该类控制并监视作为线程对象所做的所有服务器请求。从同步列表中删除元素的正确方法

这些请求线程在同步Map(使用Collections.synchronizedMap(new HashMap<Long, RequestThread>())创建)中进行跟踪。每半秒钟检查一次每个请求线程,如果它仍然活着,它会发送一个进度信息给它感兴趣的侦听器对象(请求线程的一个属性),如果它已经完成或失败,它会通知相关的侦听器,并将其本身从同步中移除Map当其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()方法的请求的映射,则删除不会被阻止,但可以更改映射和c请用我的例外。两个不同线程如何同时锁定同一个对象?不应该删除被阻止,因为它发源于不同的线程,直到更新完成?

+0

有什么异常? – 2012-02-27 17:02:35

+0

第一个代码片段中的'reqList'和第二个中的'this.reqList'引用同一个对象,对吗?没有足够的代码显示清楚。 – 2012-02-27 20:49:47

+0

Tom Hawtin - ConcurrentModificationException – BigMac66 2012-02-28 14:04:49

我打算把我的意见变成答案。

两个不同线程如何同时获得对同一个对象的锁定?

他们不能。并发修改必须在其他地方。我怀疑你的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中删除删除列表中的项目。
+0

这与我实际做的最接近。我只是停止从列表中删除线程,并让run()方法结束。我也按照建议切换到了Iterator,每次updateProgress()检查后,看看线程是否还活着 - 如果没有,那么我使用Iterator的线程安全的remove()方法。 – BigMac66 2012-03-19 15:22:49

您也需要后进行清除整个列表迭代: -

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方法。