做GUI程序的時候,通常有個后臺工作線程在努力工作,但是中間又需要一些暫停,而關閉程序的時候,必須立即結束那個線程,退出程序,也有的時候需要停止后臺線程,但不關閉程序。例如,做一個目錄監控程序,發現目錄中有文件的時候,執行一定的操作,執行完之后沒有文件了,就要暫停一下,過幾秒或幾分鐘再次檢測,這時候就要對線程進行暫停操作,如果在暫停的時候,用戶要關閉程序,就必須馬上停止線程,如果用戶需要暫停檢測,按下某個按鈕后,需要讓線程馬上停止,但再次按下某個按鈕,線程又必須馬上開始。
以前我都是通過檢測停止標記和用Thread.sleep(time)來完成的,后臺線程的每次循環都要檢查停止標記,如果發現停止標記已設定,就不再循環,退出線程,在線程內部,如果需要暫停,就執行Thread.sleep(time)。通過把線程的setDaemon(true)方法,還可以讓線程作為后臺線程,當圖形界面關閉后,線程也自動退出。
但是,這種方式有個問題,如果我需要在圖形界面上點擊按鈕來停止線程,但并不退出程序,而點擊按鈕的時候線程正處于sleep狀態,就對它沒有任何辦法,只能讓它醒過來再操作,如果sleep的時間比較長,例如1分鐘,那么點擊按鈕之后,用戶最多要等1分鐘才能把線程停下來。
當然,Thread對象有個interrupt方法,但是已經被標記為過期,一般不建議使用了。感謝評論中的提醒,Thread的interrupt()并沒有標記為過期,可以按照他的說法來操作,更為簡單。
怎么讓線程能暫停,又能隨時叫醒呢?原來Java里最原始的對象Object就自帶此功能。
每個Object都有wait(time)和notify()方法,前者就是讓擁有該Obejct的線程處于暫停狀態,后者則讓線程馬上喚醒,通過這兩個方法,就能夠滿足上述的所有要求。
首先,建立一個同步對象:
Object syncObj = new Object();
然后在線程中需要暫停的地方,調用該對象的wait(time)方法:
synchronized (syncObj) {
syncObj.wait(60*1000);
}
在圖形界面的按鈕監聽事件中,對該對象執行notify()方法:
button_1.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
thread.setStop(true);
synchronized (syncObj) {
syncObj.notify();
}
//為了等待線程退出,還可以加上以下語句:
thread.join();
}
});