本文原創,轉載請標明原處!
Thread對象可操縱一個線程,而Runnable對象代表一個可被運行的對象,必須使用Thread對象的start()方法啟動線程。啟動後,會先運行Thread對象的run()方法,這個方法未被重寫時,就會執行Runnable對象的run()方法。
主線程的入口是main()靜態方法,子線程的入口是Thread的run()方法。下圖表示Thread與Runnable的區別:
線程可以在運行中暫停,也可以從暫停中恢復運行,其暫停的目有如下列舉:
下圖表示,線程基本的暫停方式與恢復運行方式:
說明:圖中,虛線表示每次觸發時,只允許一條線程變化。Happen表示發生於其它線程,即異步觸發。
有一點不太明確,也不知道怎麼去測試,就是Auto Yield,這裡我猜想加上去的。猜想的依據是根據一篇文章《Java中的多線程你只要看這一篇就夠了》。文中寫道,並行與並發的概念與區別,思考了一下,並行是多個人同時處理各自的事,換句話說同一時刻可以處理多件事。而並發是一個人同時處理多件事,但實際上是做不到的,因為同一時刻只能處理一件事。不知道這裡,我有沒有思考有誤,如果是這樣,那麼同一時刻,就只處理著一條線程。
線程結束分為自然結束與強制中止。
自然結束,是指線程啟動時的入口方法結束,自然結束分為正常結束和異常結束。異常結束是以某個異常拋出作為起點一直往上拋出,而正常結束,需要監聽到結束的標志後,處理關閉事項,需要處理上一段時間才能真正結束。
強制中止,在運行途中的任意位置結束,什麼時候中止,線程就什麼時候結束。可以使用stop()方法強制中止,也可以把把線程設置為目標線程的守護線程,這樣線程就會以目標線程結束而強制結束。強制中止,用於不需要維護重要數據的線程,即對重要數據不產生影響。
代碼示例1:主線程以子線程對象作為同步鎖,然後使用wait(...)方法,當子線程結束時,就會喚醒wait(...)方法,從而達到主線程監聽子線程的結束。
public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(){ public void run(){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("子線程結束!"); } }; thread.start(); // 相當於thread.join(),thread線程結束時,會調用thread.notifyAll() synchronized(thread){ thread.wait(); } System.out.println("主線程結束!"); // 輸出結果: // 子線程結束! // 主線程結束! }
實際上,join(...)方法裡面,用的就是這種方式監聽子線程的結束。因此,主線程使用join(...)方法後,子線程要切忌不能使用自己的線程對象作為同步鎖,那樣會造成死鎖。
代碼示例2:主線程調用子線程的interrupt()方法,使子線程在wait()方法裡拋出異常,同時還驗證了interrupt()使子線程從wait()方法中喚醒後,依然需要同步阻塞。
public static void main(String[] args) throws InterruptedException { final Object lock = new Object(); Thread[] threads = new Thread[5]; for(int i=0; i<threads.length; i++){ final int num = i + 1; Thread thread = new Thread(){ public void run(){ synchronized(lock){ try { System.out.println("線程" + num + " -> 進入等待!"); lock.wait(); } catch (InterruptedException e) { // e.printStackTrace(); System.out.println("線程" + num + " -> " + e); } System.out.println("線程" + num + " -> 退出等待!"); try { System.out.println("線程" + num + " -> 進入睡眠!"); Thread.sleep(500); } catch (InterruptedException e) { // e.printStackTrace(); System.out.println("線程" + num + " -> " + e); } System.out.println("線程" + num + " -> 睡眠結束,退出同步塊!"); Thread.currentThread().stop(); System.out.println("線程" + num + " -> 結束沒有?"); } } }; thread.start(); threads[i] = thread; } System.out.println("主線程 -> 等待2秒!"); Thread.sleep(2000); System.out.println("主線程 -> 中止所有線程!"); for(int i=0; i<threads.length; i++){ threads[i].interrupt(); } for(int i=0; i<threads.length; i++){ threads[i].join(); } System.out.println("主線程 -> 中止所有線程結束!"); /* 輸出結果: 線程1 -> 進入等待! 線程4 -> 進入等待! 主線程 -> 等待2秒! 線程3 -> 進入等待! 線程2 -> 進入等待! 線程5 -> 進入等待! 主線程 -> 中止所有線程! 線程1 -> 退出等待! 線程1 -> 進入睡眠! 線程1 -> 睡眠結束,退出同步塊! 線程4 -> 退出等待! 線程4 -> 進入睡眠! 線程4 -> 睡眠結束,退出同步塊! 線程3 -> 退出等待! 線程3 -> 進入睡眠! 線程3 -> 睡眠結束,退出同步塊! 線程2 -> 退出等待! 線程2 -> 進入睡眠! 線程2 -> 睡眠結束,退出同步塊! 線程5 -> 退出等待! 線程5 -> 進入睡眠! 線程5 -> 睡眠結束,退出同步塊! 主線程 -> 中止所有線程結束! */ }
代碼示例3:子線程作為主線程的守護線程,因主線程的結束而強制中止。
public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(){ public void run(){ boolean run = true; while(run){ System.out.println("子線程 -> 運行中……"); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("子線程 -> 正常退出!"); } }; thread.setDaemon(true); thread.start(); System.out.println("主線程 -> 2秒後結束"); Thread.sleep(2000); System.out.println("主線程 -> 結束"); /* 運行結果: 主線程 -> 2秒後結束 子線程 -> 運行中…… 子線程 -> 運行中…… 子線程 -> 運行中…… 子線程 -> 運行中…… 子線程 -> 運行中…… 主線程 -> 結束 */ }
待續更新……