1、線程的生命週期java
線程是一個動態執行的過程,它也有一個從產生到死亡的過程。多線程
下圖顯示了一個線程完整的生命週期this
使用 new 關鍵字和 Thread 類或其子類創建一個線程對象後,該線程對象就處於新建狀態。它保持這個狀態直到程序start() 這個線程。spa
當線程對象調用了start()方法以後,該線程就進入就緒狀態。就緒狀態的線程處於就緒隊列中,要等待JVM裏線程調度器的調度。線程
若是就緒狀態的線程獲取 CPU 資源,就能夠執行 run(),此時線程便處於運行狀態。處於運行狀態的線程最爲複雜,它能夠變爲阻塞狀態、就緒狀態和死亡狀態。3d
若是一個線程執行了sleep(睡眠)、suspend(掛起)等方法,失去所佔用資源以後,該線程就從運行狀態進入阻塞狀態。在睡眠時間已到或得到設備資源後能夠從新進入就緒狀態。能夠分爲三種:code
等待阻塞:運行狀態中的線程執行 wait() 方法,使線程進入到等待阻塞狀態。對象
同步阻塞:線程在獲取 synchronized 同步鎖失敗(由於同步鎖被其餘線程佔用)。blog
其餘阻塞:經過調用線程的 sleep() 或 join() 發出了 I/O 請求時,線程就會進入到阻塞狀態。當sleep() 狀態超時,join() 等待線程終止或超時,或者 I/O 處理完畢,線程從新轉入就緒狀態。繼承
一個運行狀態的線程完成任務或者其餘終止條件發生時,該線程就切換到終止狀態
2、線程的優先級
具備較高優先級的線程對程序更重要,而且應該在低優先級的線程以前分配處理器資源。可是,線程優先級不能保證線程執行的順序,並且很是依賴於平臺
3、建立一個線程
Java 提供了三種建立線程的方法:
建立一個線程,最簡單的方法是建立一個實現 Runnable 接口的類,爲了實現 Runnable,一個類只須要執行一個方法調用 run()
1、建立一個對象類 package test_synthronized; public class Foo { int x=100; public int getX() { return x; } public int fix(int y) { synchronized(this){ x=x-y; System.out.println("線程"+Thread.currentThread().getName() + "運行結束,減小「" + y + "」,當前值爲:" + x);} return x; } } 2、建立一個線程類 package test_synthronized; /** * 線程的同步與鎖 */ public class Thread_synchronized_01 implements Runnable{ private Foo foo=new Foo(); public static void main(String[] args) { Thread_synchronized_01 syn=new Thread_synchronized_01(); Thread t1=new Thread(syn,"t1"); Thread t2=new Thread(syn,"t2"); t1.start(); t2.start(); } /* (non-Javadoc) * @see java.lang.Runnable#run() */ public void run() { for (int i = 0; i < 3; i++) { this.fix(30); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } /* * t1 : 當前foo對象的x值= 40 t2 : 當前foo對象的x值= 40 t1 : 當前foo對象的x值= -20 t2 : 當前foo對象的x值= -50 t1 : 當前foo對象的x值= -80 t2 : 當前foo對象的x值= -80 * 從結果發現,這樣的輸出值明顯是不合理的。緣由是兩個線程不加控制的訪問Foo對象並修改其數據所致。 若是要保持結果的合理性,只須要達到一個目的,就是將對Foo的訪問加以限制,每次只能有一個線程在訪問。這樣就能保證Foo對象中數據的合理性了。 在具體的Java代碼中須要完成一下兩個操做: 把競爭訪問的資源類Foo變量x標識爲private; 同步哪些修改變量的代碼,使用synchronized關鍵字同步方法或代碼。 */ System.out.println(Thread.currentThread().getName() + " : 當前foo對象的x值= " + foo.getX()); } } public int fix(int y) { return foo.fix(y); } } 3、結果 線程t1運行結束,減小「30」,當前值爲:70 t1 : 當前foo對象的x值= 70 線程t1運行結束,減小「30」,當前值爲:40 t1 : 當前foo對象的x值= 40 線程t1運行結束,減小「30」,當前值爲:10 t1 : 當前foo對象的x值= 10 線程t2運行結束,減小「30」,當前值爲:-20 t2 : 當前foo對象的x值= -20 線程t2運行結束,減小「30」,當前值爲:-50 t2 : 當前foo對象的x值= -50 線程t2運行結束,減小「30」,當前值爲:-80 t2 : 當前foo對象的x值= -80
2) 繼承Thread來建立線程
第二種方法是建立一個新的類,該類繼承 Thread 類,而後建立一個該類的實例。
繼承類必須重寫 run() 方法,該方法是新線程的入口點。它也必須調用 start() 方法才能執行。
該方法儘管被列爲一種多線程實現方式,可是本質上也是實現了 Runnable 接口的一個實例
package test_synthronized; public class Thread_synchronized_02 {
class MyThread extends Thread{ private Foo foo; /**當前值*/ private int y = 0; MyThread(String name, Foo foo, int y) { super(name); this.foo = foo; this.y = y; } public void run() { foo.fix(y); } } public static void main(String[] args) { Thread_synchronized_02 run = new Thread_synchronized_02(); Foo foo=new Foo(); MyThread t1 = run.new MyThread("線程A", foo, 10); MyThread t2 = run.new MyThread("線程B", foo, 2); MyThread t3 = run.new MyThread("線程C", foo, 3); MyThread t4 = run.new MyThread("線程D", foo, 5); t1.start(); t2.start(); t3.start(); t4.start(); } } 結果 線程線程A運行結束,減小「10」,當前值爲:90 線程線程C運行結束,減小「3」,當前值爲:87 線程線程B運行結束,減小「2」,當前值爲:85 線程線程D運行結束,減小「5」,當前值爲:80
3) 經過 Callable 和 Future 建立線程
1. 建立 Callable 接口的實現類,並實現 call() 方法,該 call() 方法將做爲線程執行體,而且有返回值。
2. 建立 Callable 實現類的實例,使用 FutureTask 類來包裝 Callable 對象,該 FutureTask 對象封裝了該 Callable 對象的 call() 方法的返回值。
3. 使用 FutureTask 對象做爲 Thread 對象的 target 建立並啓動新線程。
4. 調用 FutureTask 對象的 get() 方法來得到子線程執行結束後的返回值
package test_synthronized; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class CallableThread implements Callable<Integer> { public Integer call() throws Exception { int i = 0; for(;i<8;i++) { System.out.println(Thread.currentThread().getName()+" "+i); } return i; } public static void main(String[] args) { CallableThread callableThread=new CallableThread(); FutureTask<Integer> ft=new FutureTask<Integer>(callableThread); for(int i = 0;i < 8;i++) { System.out.println(Thread.currentThread().getName()+" 的循環變量i的值"+i); if(i==2) { new Thread(ft,"有返回值的線程").start(); } } try { System.out.println("子線程的返回值:"+ft.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } 結果 main 的循環變量i的值0 main 的循環變量i的值1 main 的循環變量i的值2 main 的循環變量i的值3 有返回值的線程 0 main 的循環變量i的值4 有返回值的線程 1 main 的循環變量i的值5 有返回值的線程 2 main 的循環變量i的值6 有返回值的線程 3 main 的循環變量i的值7 有返回值的線程 4 有返回值的線程 5 有返回值的線程 6 有返回值的線程 7 子線程的返回值:8
4、線程的三種方式的對比
1. 採用實現 Runnable、Callable 接口的方式創見多線程時,線程類只是實現了 Runnable 接口或 Callable 接口,還能夠繼承其餘類。
2. 使用繼承 Thread 類的方式建立多線程時,編寫簡單,若是須要訪問當前線程,則無需使用 Thread.currentThread() 方法,直接使用 this 便可得到當前線程。