2010-12-17 146 views
3

大家好: 基本上我需要在用戶按下終止按鈕時終止或停止正在運行的線程。此線程循環播放列表並在JTextArea上顯示每個事件。要求是當用戶按下Terminate按鈕時,我需要終止正在運行的線程,同時向ArrayList添加一個新的「終止」事件,並讓它再次運行以打印「編程終止」。下面的代碼類型的「作品」,但我在控制檯中得到了一個java.util.ConcurrentModificationException。任何人都可以幫忙Java終止或終止線程

public void startEvents() 
    { 
     terminate = false; 
     worker = new Thread(new Runnable() 
     { 
      public void run() 
      { 
       Iterator<Event> it = eventList.iterator(); 

       while (it.hasNext()) 
       { 
        waitWhileSuspended(); 
        terminatEvents(); 
        Event ev = it.next(); 
        try 
        { 
         Thread.sleep(ev.getDelayTime()); 
        } catch (InterruptedException e1) 
        { 
         e1.printStackTrace(); 
        } 
        jTextArea.append(ev.toString() + "\n"); 
        it.remove(); 
       } 
       jbStart.setEnabled(true); 
       jmiStart.setEnabled(true); 
       jbRestart.setEnabled(true); 
       jmiRestart.setEnabled(true); 
      } 
     }); 
     worker.start(); 
    } 
public void terminatEvents() 
    { 
     while(terminate) 
     { 
      Thread.yield(); 
      eventList.clear(); 
      eventList.add(new Terminate(delayTime)); 
      startEvents(); 

     } 
    } 
+0

什麼數據類型是eventList? (實際上是一個java.util.ArrayList <...>)嘗試java.util.concurrent包中的集合,看看是否有幫助。僅供參考,我通常使用某種類型的互斥體(例如java.util.concurrent.ReentrantLock)來處理這種情況。 – Merky 2010-12-17 16:38:09

回答

1

問題是你正在修改一個List並同時循環它。對於標準列表,這個行爲是未定義的,並且拋出異常。 查看java.util.concurrent軟件包的多線程使用安全集合。

1

看起來你正在修改列表(清除它,然後添加一個新的Terminate事件),同時迭代它。這就是爲什麼你得到ConcurrentModificationException

我建議你只需在你的線程對象中有一個terminate()方法,並調用它來停止打印事件列表,然後打印新的Terminate事件,而不使用列表。

0

您從2個主題中改變了Collection。默認情況下,集合是不同步的,你應該使用「同步」的關鍵字,或切換到synchronizedCollection http://download.oracle.com/javase/1.4.2/docs/api/java/util/Collections.html#synchronizedCollection(java.util.Collection)

+0

我懷疑sychronized可能是解決方案。任何人都可以告訴我如何在代碼中實現它。 – Jack 2010-12-17 17:18:03

+0

@ user528995,請顯示,你如何聲明並初始化你eventList – osgx 2010-12-17 17:31:39

+0

這是錯誤的。異常發生在單個線程上,實際上與線程無關。這個名字是用詞不當。事實上,正如其他幾位人士所說的那樣,底層列表正在發生變化,同時你正在迭代它。 – Robin 2010-12-17 18:19:36

0

的常用方法停止線程設置一些揮發性布爾標誌,我看到在你的情況下,它是terminate場。遵循正常模式,您應該在每次迭代時檢查此標誌,如while (!terminated && ...)。設置終止標誌的線程還應該把你的最終事件放在某個字段中,比如terminateEvent,如果終止在那一點上是真的,那麼你應該在循環之後檢查它(也就是說,如果線程被終止而不是正常結束)。當然,對terminateEvent的訪問應該是同步的(注意volatile在這裏可能不起作用)。

但是,由於您有要處理的事件列表,我寧願遵循另一種模式。將列表替換爲併發隊列(LinkedBlockingQueue就是一個很好的例子),然後,當您需要終止線程時,而不是設置布爾標誌,您只需清除隊列並在那裏放置終止事件。事件處理線程在處理完每個事件之後,應該檢查它是否是一個終止事件(通過使用instanceof或某種類型的getEventClass()方法),如果是,則打破循環。請注意,由於您的線程有像Thread.sleep()和可能的waitWhileSuspended()這樣的冗長操作(不管它是什麼,儘管在切換到阻塞隊列後可能不再需要它),您需要中斷()在將終止事件放入隊列後處理線程,並根據應用程序邏輯處理事件處理線程中的InterruptedException。例如,如果Thread.sleep()被中斷或者可能繼續下一次迭代,則應決定是否處理事件。

0

我會做的事情是這樣的

public void run() { 
    while (!isInterrupted()) { 
    // actual working code goes here 
    } 
} // end of life for this thread 

,然後就調用中斷()時,我想停止線程。