【JAVA秒會技術之玩轉多執行緒】多執行緒那些事兒(一)
多執行緒那些事兒(一)
現在只要出去面試,關於“Java多執行緒”的問題,幾乎沒有一家單位不問的,可見其重要性。於是博主抽空研究了一下,確實很有意思!以下是我綜合整理了網上的各種資料,和個人的一些理解,寫的一篇總結博文,僅供學習、交流。
(一)多執行緒的概念
多執行緒的概念,簡單理解:一個程序執行時產生了不止一個執行緒。
程序的概念,簡單理解:正在執行的程式的例項。
兩者之間的關係:程序就是執行緒的容器。
(二)多執行緒的好處
使用多執行緒可以提高CPU的利用率。在多執行緒程式中,一個執行緒必須等待的時候,CPU可以執行其它的執行緒而不是等待,這樣就大大提高了程式的效率。
(三)執行緒狀態轉換
1、新建狀態(New):新建立了一個執行緒物件。
2、就緒狀態(Runnable):執行緒物件建立後,其他執行緒呼叫了該物件的start()方法。該狀態的執行緒位於可執行執行緒池中,變得可執行,等待獲取CPU的使用權。
3、執行狀態(Running):就緒狀態的執行緒獲取了CPU,執行程式程式碼。
4、阻塞狀態(Blocked):阻塞狀態是執行緒因為某種原因放棄CPU使用權,暫時停止執行。直到執行緒進入就緒狀態,才有機會轉到執行狀態。阻塞的情況分三種:
①等待阻塞:執行的執行緒執行wait()方法,JVM會把該執行緒放入等待池中。
②同步阻塞
③其他阻塞:執行的執行緒執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該執行緒置為阻塞狀態。當sleep()狀態超時、join()等待執行緒終止或者超時、或者I/O處理完畢時,執行緒重新轉入就緒狀態。
5、死亡狀態(Dead):執行緒執行完了或者因異常退出了run()方法,該執行緒結束生命週期。
(五)建立執行緒的方式
建立執行緒的方式主要有兩種:
(1)一種是繼承Tread類:
package com.liyan.Test; public class MyThread extends Thread { //繼承Tread類 @Override public void run(){ //重寫run方法 try{ ....... }catch(Exception e){ e.printStackTrace(); } } public static void main(String[] args) { MyThread myThread = new MyThread(); //產生新的執行緒物件 myThread.start(); //開啟執行緒 } }
(2)一種是實現Runnable介面
package com.liyan.gcTest; public class MyThread implements Runnable { //實現Runnable介面 @Override public void run(){ //重寫run方法 try{ ....... }catch(Exception e){ e.printStackTrace(); } } public static void main(String[] args) { MyThread myThread = new MyThread(); //產生新的執行緒物件 Thread thread = new Thread(myThread); //將建立的執行緒作為引數傳入 thread.start(); //開啟執行緒 } }
(六)終止執行緒的方式
1. 使用退出標誌終止執行緒
當run方法執行完後,執行緒就會退出。但有時run方法是永遠不會結束的。如在服務端程式中使用執行緒進行監聽客戶端請求,或是其他的需要迴圈處理的任務。在這種情況下,一般是將這些任務放在一個迴圈中,如while迴圈。如果想讓迴圈永遠執行下去,可以使用while(true){……}來處理。但要想使while迴圈在某一特定條件下退出,最直接的方法就是設一個boolean型別的標誌,並通過設定這個標誌為true或false來控制while迴圈是否退出。下面給出了一個利用退出標誌終止執行緒的例子。
package com.liyan.thread; public class ThreadDemo extends Thread { public volatile boolean exit = false; public void run() { while (!exit); } public static void main(String[] args) throws Exception { ThreadDemo thread = new ThreadDemo(); thread.start(); sleep(5000); // 主執行緒延遲5秒 thread.exit = true; // 終止執行緒thread thread.join(); System.out.println("執行緒退出!"); } }
2. 使用stop方法終止執行緒 在上面程式碼中定義了一個退出標誌exit,當exit為true時,while迴圈退出,exit的預設值為false.在定義exit時,使用了一個Java關鍵字volatile,這個關鍵字的目的是使exit同步,也就是說在同一時刻只能由一個執行緒來修改exit的值。
使用stop方法可以強行終止正在執行或掛起的執行緒。我們可以使用如下的程式碼來終止執行緒:
thread.stop();
3. 使用interrupt方法終止執行緒 雖然使用上面的程式碼可以終止執行緒,但使用stop方法是很危險的,就象突然關閉計算機電源,而不是按正常程式關機一樣,可能會產生不可預料的結果,因此,並不推薦使用stop方法來終止執行緒。
使用interrupt方法來終端執行緒可分為兩種情況:
(1)執行緒處於阻塞狀態,如使用了sleep方法。
(2)使用while(!isInterrupted()){……}來判斷執行緒是否被中斷。
在第一種情況下使用interrupt方法,sleep方法將丟擲一個InterruptedException例外,而在第二種情況下執行緒將直接退出。下面的程式碼演示了在第一種情況下使用interrupt方法。
package com.liyan.thread; public class ThreadDemo extends Thread { public void run() { try { sleep(50000); // 延遲50秒 } catch (InterruptedException e) { System.out.println(e.getMessage()); } } public static void main(String[] args) throws Exception { Thread thread = new ThreadDemo(); thread.start(); System.out.println("在50秒之內按任意鍵中斷執行緒!"); System.in.read(); thread.interrupt(); thread.join(); System.out.println("執行緒已經退出!"); } }
輸出結果:
在50秒之內按任意鍵中斷執行緒! liyan sleep interrupted 執行緒已經退出!
在呼叫interrupt方法後,sleep方法丟擲異常,然後輸出錯誤資訊:sleep interrupted.
注意:在Thread類中有兩個方法可以判斷執行緒是否通過interrupt方法被終止。一個是靜態的方法interrupted(),一個是非靜態的方法isInterrupted(),這兩個方法的區別是interrupted用來判斷當前線是否被中斷,而isInterrupted可以用來判斷其他執行緒是否被中斷。因此,while(!isInterrupted())也可以換成while(!Thread.interrupted())。
【參考:http://www.bitscn.com/pdb/java/200904/161228_3.html】
(七)Daemon執行緒
在Java中有兩類執行緒:User Thread(使用者執行緒)、Daemon Thread(守護執行緒)
用個比較通俗的比如,任何一個守護執行緒都是整個JVM中所有非守護執行緒的保姆:
只要當前JVM例項中尚存在任何一個非守護執行緒沒有結束,守護執行緒就全部工作;只有當最後一個非守護執行緒結束時,守護執行緒隨著JVM一同結束工作。
Daemon的作用是為其他執行緒的執行提供便利服務,它的優先順序比較低,用於為系統中的其它物件和執行緒提供服務。守護執行緒不依賴於終端,但是依賴於系統,與系統“同生共死”。
守護執行緒最典型的應用就是GC (垃圾回收器),當我們的程式中不再有任何執行的hread,程式就不會再產生垃圾,垃圾回收器也就無事可做,所以當垃圾回收執行緒是JVM上僅剩的執行緒時,垃圾回收執行緒會自動離開。它始終在低級別的狀態中執行,用於實時監控和管理系統中的可回收資源。
User和Daemon兩者幾乎沒有區別,唯一的不同之處就在於虛擬機器的離開:如果User Thread已經全部退出執行了,只剩下Daemon Thread存在了,虛擬機器也就退出了。 因為沒有了被守護者,Daemon也就沒有工作可做了,也就沒有繼續執行程式的必要了。
// 設定 daemonThread 為 守護執行緒,default false(非守護執行緒) daemonThread.setDaemon(true); // 驗證當前執行緒是否為守護執行緒,返回 true 則為守護執行緒 daemonThread.isDaemon();
(1)thread.setDaemon(true)必須在thread.start()之前設定,否則會跑出一個IllegalThreadStateException異常。你不能把正在執行的常規執行緒設定為守護執行緒。 這裡有幾點需要注意:
(2)在Daemon執行緒中產生的新執行緒也是Daemon的。
(3)不要認為所有的應用都可以分配給Daemon來進行服務,比如讀寫操作或者計算邏輯。因為你不可能知道在所有的User完成之前,Daemon是否已經完成了預期的服務任務。一旦User退出了,可能大量資料還沒有來得及讀入或寫出,計算任務也可能多次執行結果不一樣。這對程式是毀滅性的。造成這個結果理由已經說過了:一旦所有User Thread離開了,虛擬機器也就退出執行了。
【參考:http://blog.csdn.net/shimiso/article/details/8964414】