2016-01-05 70 views
0

我已經對我的問題做了一些研究,我仍然無法解決手頭的問題。線程對我來說是新的,所以我很難理解。在我的程序中,我啓動了一個在定時傳輸文件的線程。此程序中的SWT正用於GUI。在我的主UI代碼中,我有一個暫停和播放按鈕。播放按鈕和相關代碼:IllegalMonitorStateException當試圖等待()一個線程

playButton.addSelectionListener(new SelectionAdapter() { 
     @Override 
     public void widgetSelected(SelectionEvent e) { 

      if(isRunning){ 
       // TODO implies runningThread is waiting, notify it 
      }else{ 
       playButton.setEnabled(false); 
       pauseButton.setEnabled(true); 
       try { 
        play(); 
       } catch (IOException e1) { 
        e1.printStackTrace(); 
       } 
      } 
     } 
    }); 

public void play() throws IOException{ 

    if(controller.timMan.getEventSendPreferences().equalsIgnoreCase("manual")){ 
     isRunning = true; 
     manualThread.start(); 


    }else if(controller.timMan.getEventSendPreferences().equalsIgnoreCase("timed")){ 
     isRunning = true; 
     timerThread.start(); 
    } 

    return; 
} 

timerThread實現爲這樣:

timerThread = new Thread(new RunOnTimer(controller)); 

public static class RunOnTimer implements Runnable{ 

    ScriptController controller; 

    public RunOnTimer(ScriptController c){ 
     controller = c; 
    }; 

    @Override 
    public void run(){ 
     try{ 
      synchronized(this){ 
       controller.runOnTimer(); 
      } 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
}; 

,並在這裏運行被稱爲runOnTimer()函數:

public void runOnTimer() throws IOException{ 

    for(File f : dirMan.getEventFileList()){ 
     int randomTimeValue = 0; 
      int range = timMan.getUpperTimerBound() - timMan.getLowerTimerBound(); 
      if(range > 0) 
       randomTimeValue = (new Random().nextInt(timMan.getUpperTimerBound() - timMan.getLowerTimerBound()) + 0); 

      try { 
       Thread.sleep(1000 * (randomTimeValue + timMan.getLowerTimerBound())); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 

      dirMan.updateFileCounts(); 
      System.out.println("Event " + dirMan.getSentEventFiles()); 
      File dest = new File(dirMan.getDestinationFolder() + "\\" + f.getName()); 
      Files.copy(f.toPath(), dest.toPath(), StandardCopyOption.REPLACE_EXISTING); 
    } 
    updateFileCounts(); 
    return; 
} 

問題開始不同時該線程正在運行,但是當我調用線程以等待暫停按鈕偵聽器實現時。

pauseButton.addSelectionListener(new SelectionAdapter() { 

     @Override 
     public void widgetSelected(SelectionEvent e) { 
      if(timerThread.isAlive()){ 
       try { 
        timerThread.wait(); 
       } catch (InterruptedException e1) { 
        // TODO Auto-generated catch block 
        e1.printStackTrace(); 
       } 
      } 

      pauseButton.setEnabled(false); 
      playButton.setEnabled(true); 
     } 

    }); 

在我看過的其他問題,並從什麼谷歌已經結果表明,同步通常是問題,當談到這個錯誤。與獲取對象監視器的所有者有關。再次,我沒有使用線程,所以這個對象監視器的概念對我來說是個謎。 因此,在閱讀這些內容之後,我嘗試使用RunOnTimer類的run()方法中的synchronized,但在嘗試「暫停」(即,使線程等待)時似乎沒有改變任何內容。

它是什麼我失蹤或做錯了。該程序將正常工作,線程將像我預期的那樣運行,除了錯誤當然。

+0

您能否提供錯誤的堆棧跟蹤?另外,在void方法結束時不需要'return'調用。 – RamV13

回答

0

如果您Object.wait閱讀文檔,你會看到這個信息的關鍵部分:

當前線程必須擁有該對象的監視器。

這意味着你必須在你調用wait的對象上有一個同步塊。文檔,實際上,有一個很好的僞代碼顯示的wait

正確使用你的情況:

synchronized(timerThread) 
{ 
    while(shouldStillWait()) 
    { 
    timerThread.wait(); 
    } 
} 

boolean shouldStillWait() 
{ 
    ... 
} 

也應該有一個地方的地方在你的代碼調用timerThread.notify(),沒有它,你會等待永遠。

這就是說,因爲Java 5的,有更好的內置同步原語,使過去使用的wait/notify相當多的事情。

這是一件好事,因爲wait/notify是非常脆弱和容易出錯。

我建議您閱讀Brian Goetz的優秀'Java Concurrency in Practice',以熟悉新的同步原語和良好風格的多線程編程。

+0

我在哪裏添加這條語句?在暫停按鈕偵聽器或按鈕調用以啓動線程的方法?我擔心while循環會鎖定UI。 –

0

問題是,您不能在暫停按鈕的偵聽器中調用timerThread.wait(),因爲整個方法體在應用程序的事件處理線程中運行,因此該方法不擁有timerThread的對象監視器。

因此,爲了實現您所需的功能,您應該有一個變量來維護應用程序的狀態(即是否暫停)。

在主應用程序類,你可以聲明boolean變量

private boolean paused; 

您的暫停按鈕事件處理方法,在裏面你應該刪除

 if(timerThread.isAlive()){ 
      try { 
       timerThread.wait(); 
      } catch (InterruptedException e1) { 
       // TODO Auto-generated catch block 
       e1.printStackTrace(); 
      } 
     } 

,取而代之的是

paused = !paused; // to just toggle the state 
timerThread.notifyAll(); // to notify waiting thread 

然後在你的Runnable這將是

@Override 
public void run(){ 
    try{ 
     while (paused) { 
      synchronized(this) { 
       wait(); // keep sleeping this thread while the state is still paused 
      } 
     } 
     controller.runOnTimer(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 
+0

儘管如此,即使我實現了上面顯示的方式,它實際上並沒有像我打算的那樣暫停線程。 –

+0

我編輯了相應的答案。希望這有助於! – RamV13