java使用Thread類表明線程,全部的線程對象都必須是Thread類或其子類的實例。每條線程的做用是完成必定的任務,實際上就是執行一段程序流(一段順序流的代碼)。Java使用run方法來封裝這樣一段程序。 java
/**繼承Thread來建立線程類*/ public class FirstThread extends Thread { private int i; //重寫run方法,run方法的方法體就是線程執行體 public void run() { for(;i<10;i++){ System.out.println(this.getName()+":"+i); } } public static void main(String []args){ for(int i=0;i<20;i++){ System.out.println(Thread.currentThread().getName()+" .."+i); if(i==10){ System.out.println("--------------------------------------------"); new FirstThread().start(); new FirstThread().start(); System.out.println("---------------------------------------------"); } } } } 結果:紅色部分每次運行都不一致,由於多線程也是併發的 main ..0 main ..1 main ..2 main ..3 main ..4 main ..5 main ..6 main ..7 main ..8 main ..9 main ..10 -------------------------------------------- Thread-0:0 --------------------------------------------- Thread-1:0 Thread-1:1 Thread-1:2 Thread-1:3 Thread-0:1 Thread-1:4 Thread-1:5 main ..11 Thread-1:6 Thread-1:7 Thread-1:8 Thread-1:9 Thread-0:2 Thread-0:3 main ..12 main ..13 ......
總結 :從上面結果能夠看出Thread-0和Thread-1兩條線程輸出的i變量都不連續(注意:i變量是FirestThread的實例屬性,而不是局部變量,但由於程序每次建立線程都會建立一個FirstThread對象,因此Thread-0和Thread-1不能共享該實例屬性)。 多線程
使用繼承Thread類的方法來建立線程類,多條線程之間沒法共享線程類的實例變量。 併發
public class SecondThread implements Runnable { private int i; public void run() { for(;i<20;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } public static void main(String [] args){ for(int i=0;i<20;i++){ System.out.println(Thread.currentThread().getName()+" .."+i); if(i==10){ SecondThread st=new SecondThread(); //經過new Thread( Runable target,String name)來建立新線程 new Thread(st,"線程1").start(); new Thread(st,"線程2").start(); } } } 結果:紅色部分每次運行都不一致,由於多線程也是併發的 main ..0 main ..1 main ..2 main ..3 main ..4 main ..5 main ..6 main ..7 main ..8 main ..9 main ..10 -------------------------------------------- 線程1:0 -------------------------------------------- 線程1:1 線程2:1 線程2:3 main ..11 線程2:4 線程2:5 線程2:6 線程1:2 線程2:7 線程2:9 線程2:10 線程2:11 線程2:12 線程2:13 main ..12 線程2:14 線程2:15 線程2:16 線程2:17 線程1:8 線程2:18 main ..13 main ..14 線程1:19 main ..15 main ..16 main ..17 。。。。
總結:根據源代碼中Thread類構造方法 Ruanalbe接口對象target只能做爲參數傳遞到Thread構造方法中,因此多個線程能夠共用一個Runnable對象,由於都用同一個Runnable對象因此在Runnable實現類的實例變量也能夠共享了。 ide
因此Runable很是適合多個相同線程來處理同一份資源的狀況。 測試
1.New新建 :當線程被建立時,該線程處於新建狀態,此時它和其餘java對象同樣,僅僅由Java虛擬機爲其分配了內存,並初始化了其成員變量的值。(此時的線程沒有表現出任何表現出任何線程的動態特徵,程序也不會執行線程的線程執行體)new Thread()||new Thread(Runnable target,String name)。 this
2.Runnable就緒:就緒也就是說啓動線程,可是啓動線程使用start方法,而不是run方法!永遠不要調用線程對象的run()方法!調用start方法來啓動線程,系統會將該run方法當成線程執行體來處理。若是直接調用線程對象的run方法。則run方法會當即執行,且在這個run方法的執行體未執行結束前其餘線程沒法併發執行(即系統會將run方法當作一個普通對象的普通方法,而不是線程執行體對待) spa
附1:若是有一個主線程,一個子線程。當根據邏輯代碼該調用子線程時不必定會當即調用,爲了想在子線程start()後當即調用子線程,能夠考慮使用Thread.sleep(1),這樣會讓當前線程(主線程)睡眠1毫秒,由於cpu在這1毫秒中是不會休息的,這樣就會去執行一條處於就緒狀態的線程。 線程
附2:不能對已經處於就緒狀態的線程,再次使用start() unix
3.Running 運行:當處於就緒狀態時,該線程得到cpu,執行體開始運行,就處於運行狀態了。
4.Blocked 阻塞:線程不可能一直處於運行狀態(線程執行體足夠短,瞬間就能夠完成的線程排除),線程會在運行過程當中須要被中斷,由於是併發,目的是會讓其餘線程得到執行的機會,線程的調度細節取決於OS採用的策略。(搶佔式調度xp win7 linux unix..)。若是是一些特殊的小型設備可能採用 協做式調度(只有線程本身調用它的sleep()或yield()纔會放棄所佔用的資源)。
5.Dead死亡:根據上圖所示。測試測試某條線程是否已經死亡,能夠調用線程對象的isAlive()方法,當線程處於就緒,運行,阻塞時,返回true。線程處於新建,死亡時返回false。
不能對已經死亡的線程調用start()方法使它從新啓動,死亡就是死亡,是不能再次做爲線程執行的。
當主線程結束時候,其餘線程不受任何影響,並不會隨之結束。一旦子線程啓動起來後,它就擁有和主線程相同的地位,它不會受到主線程的影響。
讓一個線程等待另外一個線程完成的方法:join()。當在某個程序執行流中調用其餘線程的join()方法,那該執行流對應的線程就會阻塞,知道被join()加入的join線程完成爲止。join方法一般有使用線程的程序調用,將大問題劃分紅許多小問題,每一個小問題分配一個線程。當全部的小問題都獲得處理後,再調用 主線程來進一步操做(Thread t=new Thread();t.start();t.join簡單來講就是加入到t線程。等t線程執行完成後纔會返回出來執行線程。)
Join方法有三種重用形式:
Join():等待被join的線程執行完成
Join(long millis):等待join線程的時間最長爲millis毫秒,若是在這個時間內,被join的線程尚未執行結束則再也不等待)
Join(long millis,int nanos)千分之一毫秒(不用)
Code:
public class JoinThread implements Runnable{ @Override public void run() { for(int i=0;i<5;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } public static void main(String [] args) throws InterruptedException{ //實例化一個Runnable JoinThread jt=new JoinThread(); //建立一個線程 new Thread(jt).start(); for(int i=0;i<10;i++){ if(i==3){ Thread th=new Thread(jt); //啓動第二個線程 th.start(); //main的線程中調用了th線程的join方法 //讓第二個線程執行完成後再執行main th.join(); } System.out.println(Thread.currentThread().getName()+":"+i); } } } 結果: Thread-0:0 Thread-0:1 Thread-0:2 main:0 main:1 Thread-0:3 main:2 Thread-0:4 Thread-1:0 Thread-1:1 Thread-1:2 Thread-1:3 Thread-1:4 main:3 main:4 main:5 main:6 main:7 main:8 main:9
Code:
public class DaemonThread implements Runnable{ @Override public void run() { for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } public static void main(String [] args){ //要將前臺線程轉換成後臺線程,須要在該線程剛新建還未start()以前轉換。main線程也是前臺線程 //全部前臺線程死亡時,後臺線程也就隨之死亡。 DaemonThread dt=new DaemonThread(); Thread td=new Thread(dt,"線程1"); System.out.println("main方法是不是後臺線程"+Thread.currentThread().isDaemon()); System.out.println("td線程最初是不是後臺線程"+td.isDaemon()); //指定td爲後臺線程 td.setDaemon(true); System.out.println("td線程執行setDaemon方法後是不是後臺線程"+td.isDaemon()); //就緒啓動後臺線程 td.start(); for(int i=0;i<5;i++){ System.out.println(Thread.currentThread().getName()+" "+i); } } } 結果:只要前臺線程結束,後臺線程也會隨之結束,並非立刻結束 main方法是不是後臺線程false td線程最初是不是後臺線程false td線程執行setDaemon方法後是不是後臺線程true main 0 main 1 線程1:0 線程1:1 main 2 線程1:2 線程1:3 main 3 線程1:4 線程1:5 main 4 線程1:6 線程1:7 線程1:8 線程1:9 線程1:10 線程1:11 線程1:12 線程1:13
/** * 線程睡眠:sleep有兩種重載形式: * static void sleep(long millis) * static void sleep(long millis,int nanos) * */ public class SleepThread { public static void main(String [] args) throws InterruptedException{ for(int i=0;i<5;i++){ System.out.println("線程:"+Thread.currentThread().getName()+"當前時間:"+new Date()); //讓當前線程暫停2秒 Thread.sleep(2000); } } } 結果: 線程:main當前時間:Fri Nov 04 18:51:33 CST 2011 線程:main當前時間:Fri Nov 04 18:51:35 CST 2011 線程:main當前時間:Fri Nov 04 18:51:37 CST 2011 線程:main當前時間:Fri Nov 04 18:51:39 CST 2011 線程:main當前時間:Fri Nov 04 18:51:41 CST 2011
/** * yield()方法是一個和sleep方法有點相似的靜態方法。yield也可讓當前正在執行的線程暫停 * 但它不會阻塞該線程,它只是將該線程轉入就緒狀態。yield只是讓當前線程暫停一下子,讓系統的 * 調度器從新調度一次(徹底可能的狀況是:當一個線程調用了yield方法暫停以後,線程調度器又立刻 * 將其調度出來從新執行。) * 實際上,當前線程調用了yield方法後,只有優先級和當前線程相同,甚至優先級高於當前線程的處於 * 就緒狀態的線程纔會得到執行機會。 * */ public class YieldThread implements Runnable{ @Override public void run() { for(int i=0;i<50;i++){ System.out.println(Thread.currentThread().getName()+":"+i); if(i==20){ Thread.yield(); } } } public static void main(String [] args){ //啓動第一條子線程 Thread td1=new Thread(new YieldThread(),"線程1"); //最高級 //td1.setPriority(Thread.MAX_PRIORITY); //啓動第二條子線程 Thread td2=new Thread(new YieldThread(),"線程2"); //最低級 td2.setPriority(Thread.MIN_PRIORITY); td1.start(); td2.start(); System.out.println(Thread.currentThread().getName()); } }
總結:sleep和yield區別
A.sleep方法暫停當前線程後,會給其餘線程執行機會,不會理會其餘線程的優先級。而yield只會給優先級>=當前優先級的線程執行機會
B.Sleep方法會將線程轉入阻塞狀態,知道通過阻塞時間纔會轉入就緒狀態。而yield是不會將線程轉入阻塞狀態的,它只是強制當前線程進入就緒狀態。
C.Sleep會拋出InterruptedException異常。而yield沒有聲明任何異常
D.Sleep方法比yield方法有更好的移植性。
E.一般不依靠yield來控制併發線程控制