參考:http://lavasoft.blog.51cto.com/62575/99150
http://blog.csdn.net/baby_newstar/article/details/6783752
http://www.runoob.com/java/java-multithreading.html
1.操作系統中的進程和線程
進程是指一個內存中運行的應用程序,每個進程都有自己獨立的一塊內存空間,一個進程中可以啟動多個線程。比如在Windows系統中,一個運行的exe就是一個進程。
線程是指進程中的一個執行流程,一個進程中可以運行多個線程。比如java.exe進程中可以運行很多線程。線程總是屬於某個進程,進程中的多個線程共享進程的內存。
2.java中的線程
一個Java應用總是從main()方法開始運行,mian()方法運行在一個線程內,它被稱為主線程。
其他線程通過使用java.lang.Thread類或者java.lang.Runnable接口編寫代碼來定義、實例化和啟動新線程。
線程又分用戶線程和守護線程。只有通過設置setDaemon(true)的線程才是守護線程。用戶線程的生命周期由該線程自定義,比如while(true)一直執行。守護線程的生命周期是由創造它的線程決定的,父線程死掉了,它也就立即死亡而不管是否有任務還沒有執行。抽象的理解就是:守護線程是工蜂,蜂后死掉后也會跟着死掉。
3.線程的生命周期
- 新建狀態:使用 new 關鍵字和 Thread 類或其子類建立一個線程對象后,該線程對象就處於新建狀態。它保持這個狀態直到程序 start() 這個線程。
- 就緒狀態:當線程對象調用了start()方法之后,該線程就進入就緒狀態。就緒狀態的線程處於就緒隊列中,要等待JVM里線程調度器的調度。
- 運行狀態:如果就緒狀態的線程獲取 CPU 資源,就可以執行 run(),此時線程便處於運行狀態。處於運行狀態的線程最為復雜,它可以變為阻塞狀態、就緒狀態和死亡狀態。
- 阻塞狀態:如果一個線程執行了sleep(睡眠)、suspend(掛起)等方法,失去所占用資源之后,該線程就從運行狀態進入阻塞狀態。在睡眠時間已到或獲得設備資源后可以重新進入就緒狀態。
- 死亡狀態:一個運行狀態的線程完成任務或者其他終止條件發生時,該線程就切換到終止狀態。
- 如圖:
4.創建一個線程
Java提供了兩種創建線程方法:
- 通過實現Runable接口;
- 通過繼承Thread類本身。
1、如果是擴展java.lang.Thread類的線程,則直接new即可。
Thread(Runnable target, String name)
Thread(ThreadGroup group, Runnable target)
Thread(ThreadGroup group, Runnable target, String name)
Thread(ThreadGroup group, Runnable target, String name, long stackSize)
4.1實現Runnable接口

package com.test.java.thread; /** * 學習線程 * Created by mrf on 2016/2/25. */ public class NewThread implements Runnable{ Thread thread; NewThread(){ //創建第二個新線程 thread = new Thread(this,"Demo Thread"); System.out.println("我是實現Runnable接口的類,我被創建了。而且我開始創建另一個線程(name,priority,groupname):"+thread); thread.start(); } //第二個線程入口 @Override public void run() { System.out.println("------------------我是實現Runnable接口的運行入口,我要開始運行了。-----------------------"); try { for (int i = 0; i < 5; i++) { System.out.println("Child Thread:"+i); //暫停線程 Thread.sleep(50); } } catch (InterruptedException e) { System.out.println("Child interrupted"); e.printStackTrace(); } System.out.println("---------------我這個線程就要運行結束了.------------------------"); } } class ThreadDemo{ public static void main(String[] args) { System.out.println("===========main線程開始運行。=============="); System.out.println("當前運行的是main線程:"+Thread.currentThread()); new NewThread();//創建一個新線程 try { for (int i =0; i<5; i++){ System.out.println("main thread:"+i); Thread.sleep(100); } } catch (InterruptedException e) { System.out.println("main thread interruped."); e.printStackTrace(); } System.out.println("==============main線程運行結束.================"); } }
運行結果:
===========main線程開始運行。==============
當前運行的是main線程:Thread[main,5,main]
我是實現Runnable接口的類,我被創建了。而且我開始創建另一個線程(name,priority,groupname):Thread[Demo Thread,5,main]
main thread:0
------------------我是實現Runnable接口的運行入口,我要開始運行了。-----------------------
Child Thread:0
Child Thread:1
Child Thread:2
main thread:1
Child Thread:3
Child Thread:4
main thread:2
---------------我這個線程就要運行結束了.------------------------
main thread:3
main thread:4
Disconnected from the target VM, address: '127.0.0.1:4625', transport: 'socket'
==============main線程運行結束.================
分析:
java程序從main線程開始,這是主線程。然后new NewThread(),創建了這個叫做NewThread的類,這個類的構造方法里面又調用了另一個線程,即從這里開始調用新線程了。main線程和新線程的優先級都為5,因此輪換使用cpu,所以才會出現交替打印的現象。
問題:上面的結果顯示自線程運行結束后main線程才結束,那么子線程的生命周期和main有關嗎?
答案是否定的,下面我將子線程的運行時間加長就會看到結果。

package com.test.java.thread; /** * 學習線程 * Created by mrf on 2016/2/25. */ public class NewThread implements Runnable{ Thread thread; NewThread(){ //創建第二個新線程 thread = new Thread(this,"Demo Thread"); System.out.println("我是實現Runnable接口的類,我被創建了。而且我開始創建另一個線程(name,priority,groupname):"+thread); thread.start(); } //第二個線程入口 @Override public void run() { System.out.println("------------------我是實現Runnable接口的運行入口,我要開始運行了。-----------------------"); try { for (int i = 0; i < 5; i++) { System.out.println("Child Thread:"+i); //暫停線程 Thread.sleep(500); } } catch (InterruptedException e) { System.out.println("Child interrupted"); e.printStackTrace(); } System.out.println("---------------我這個線程就要運行結束了.------------------------"); } } class ThreadDemo{ public static void main(String[] args) { System.out.println("===========main線程開始運行。=============="); System.out.println("當前運行的是main線程:"+Thread.currentThread()); new NewThread();//創建一個新線程 try { for (int i =0; i<5; i++){ System.out.println("main thread:"+i); Thread.sleep(100); } } catch (InterruptedException e) { System.out.println("main thread interruped."); e.printStackTrace(); } System.out.println("==============main線程運行結束.================"); } }
結果:
===========main線程開始運行。============== 當前運行的是main線程:Thread[main,5,main] 我是實現Runnable接口的類,我被創建了。而且我開始創建另一個線程(name,priority,groupname):Thread[Demo Thread,5,main] main thread:0 ------------------我是實現Runnable接口的運行入口,我要開始運行了。----------------------- Child Thread:0 main thread:1 main thread:2 main thread:3 main thread:4 ==============main線程運行結束.================ Child Thread:1 Child Thread:2 Child Thread:3 Child Thread:4 ---------------我這個線程就要運行結束了.------------------------
注意我將子線程的睡眠時間改成了500.這樣,main線程執行完畢后子線程仍然繼續執行。這個有點可以使我們在做業務代碼時將不要緊的代碼多線程異步執行,優化運行體驗和效率。
4.2守護線程演示
還是上面的代碼,我將子線程設置為守護線程,並且設置子線程運行時間>main線程,看是否main線程運行結束后守護線程直接死掉。這里注意,setDaemon一定要放在start前,因為會判斷isAlive,如果活着就不能設置為守護線程了,因為已經在運行了。

package com.test.java.thread; /** * 學習線程 * Created by mrf on 2016/2/25. */ public class NewThread implements Runnable{ Thread thread; NewThread(){ //創建第二個新線程 thread = new Thread(this,"Demo Thread"); System.out.println("我是實現Runnable接口的類,我被創建了。而且我開始創建另一個線程(name,priority,groupname):"+thread); thread.setDaemon(true); thread.start(); } //第二個線程入口 @Override public void run() { System.out.println("------------------我是實現Runnable接口的運行入口,我要開始運行了。-----------------------"); try { for (int i = 0; i < 5; i++) { System.out.println("Child Thread:"+i); //暫停線程 Thread.sleep(500); } } catch (InterruptedException e) { System.out.println("Child interrupted"); e.printStackTrace(); } System.out.println("---------------我這個線程就要運行結束了.------------------------"); } } class ThreadDemo{ public static void main(String[] args) { System.out.println("===========main線程開始運行。=============="); System.out.println("當前運行的是main線程:"+Thread.currentThread()); new NewThread();//創建一個新線程 try { for (int i =0; i<5; i++){ System.out.println("main thread:"+i); Thread.sleep(100); } } catch (InterruptedException e) { System.out.println("main thread interruped."); e.printStackTrace(); } System.out.println("==============main線程運行結束.================"); } }
運行結果:
===========main線程開始運行。============== 當前運行的是main線程:Thread[main,5,main] 我是實現Runnable接口的類,我被創建了。而且我開始創建另一個線程(name,priority,groupname):Thread[Demo Thread,5,main] main thread:0 ------------------我是實現Runnable接口的運行入口,我要開始運行了。----------------------- Child Thread:0 main thread:1 main thread:2 main thread:3 main thread:4 Child Thread:1 Disconnected from the target VM, address: '127.0.0.1:6928', transport: 'socket' ==============main線程運行結束.================
和上一個測試對比相當明顯,child thread僅僅打印了一次就結束了,說明它死掉了,說明創建他的main線程死掉后,她跟着死掉。
4.3繼承Thread
通過繼承Thread,重寫run方法來實現多線程。下面是實例,另外上面的代碼寫的不太清楚,start方法應該放外面來調用比較好。

package com.test.java.thread; /** * 多線程學習---繼承Thread * Created by mrf on 2016/2/25. */ public class NewThreadExtend extends Thread{ NewThreadExtend(){ //創建第二個線程 super("Demo Thread."); System.out.println("子線程:"+this); } //通過重寫run方法來實現線程業務邏輯 public void run(){ System.out.println("-------我是子線程,我開始運行----------------"); try { for (int i = 0; i < 5; i++) { System.out.println("子線程:"+i); Thread.sleep(50); } } catch (InterruptedException e) { System.out.println("子線程interrupted"); e.printStackTrace(); } System.out.println("-------我是子線程,我結束運行----------------"); } } class ExtendThread{ public static void main(String[] args) { System.out.println("========我是main線程,我開始運行:"+Thread.currentThread()); NewThreadExtend newThreadExtend = new NewThreadExtend(); newThreadExtend.start(); try { for (int i = 0; i < 5; i++) { System.out.println("main線程:"+i); Thread.sleep(100); } } catch (InterruptedException e) { System.out.println("main線程interrupted."); e.printStackTrace(); } System.out.println("========main線程運行結束====================="); } }
運行結果:

========我是main線程,我開始運行:Thread[main,5,main] 子線程:Thread[Demo Thread.,5,main] main線程:0 -------我是子線程,我開始運行---------------- 子線程:0 子線程:1 main線程:1 子線程:2 子線程:3 子線程:4 main線程:2 -------我是子線程,我結束運行---------------- main線程:3 main線程:4 Disconnected from the target VM, address: '127.0.0.1:11051', transport: 'socket' ========main線程運行結束=====================
5.Thread的方法
方法就不在這里贅述了,普通方法必須由thread對象來調用,比如start,靜態方法可以由Thread來調用。這里再說下start,start執行的是run方法,初學的時候會很納悶,因為沒有深入源碼。好吧,我也沒深入,但大概看到start是加入group,然后調用start0,start0是個native method。啥是native method?看到介紹說是調用其他語言實現的代碼,到這里就是jvm干的事情了,我們不知道做了什么。不過可以猜測是和操作系統交互,獲取進程資源之類的吧,然后應該回調run方法。總之,要理解start就是在執行run方法,不過start看起來像是代理而已。
public void start()
使該線程開始執行;Java 虛擬機調用該線程的 run 方法。
6.線程的幾個概念
在多線程編程時,你需要了解以下幾個概念:
- 線程同步
- 線程間通信
- 線程死鎖
- 線程控制:掛起、停止和恢復
7.線程同步和鎖下次學習