轉載出處:http://blog.csdn.net/evankaka,林炳文Evankaka原創作品
寫在前面的話:此文只能說是java多線程的一個入門,其實Java裡頭線程完全可以寫一本書了,但是如果最基本的你都學掌握好,又怎麼能更上一個台階呢。如果你覺得此文很簡單,那推薦你看看Java並發包的的線程池(Java並發編程與技術內幕:線程池深入理解),或者看這個專欄:Java並發編程與技術內幕。你將會對Java裡頭的高並發情境下的線程有更加深刻的理解。
本文主要講了java中多線程的使用方法、線程同步、線程資料傳遞、線程狀態及相應的一些線程函數用法、概述等。在這之前,首先讓我們來瞭解下在作業系統中進程和線程的區別:
進程:每個進程都有獨立的代碼和資料空間(進程上下文),進程間的切換會有較大的開銷,一個進程包含1–n個線程。(進程是資源分派的最小單位)
線程:同一類線程共用代碼和資料空間,每個線程有獨立的運行棧和程式計數器(PC),線程切換開銷小。(線程是cpu調度的最小單位)
線程和進程一樣分為五個階段:建立、就緒、運行、阻塞、終止。
多進程是指作業系統能同時運行多個任務(程式)。
多線程是指在同一程式中有多個順序流在執行。
在java中要想實現多線程,有兩種手段,一種是繼續Thread類,另外一種是實現Runable介面.(其實準確來講,應該有三種,還有一種是實現Callable介面,並與Future、線程池結合使用,此文這裡不講這個,有興趣看這裡Java並發編程與技術內幕:Callable、Future、FutureTask、CompletionService )
一、擴充java.lang.Thread類
這裡繼承Thread類的方法是比較常用的一種,如果說你只是想起一條線程。沒有什麼其它特殊的要求,那麼可以使用Thread.(筆者推薦使用Runable,後頭會說明為什麼)。下面來看一個簡單的執行個體
package com.zhihua.Thread;/** * 繼承Thread類,簡單的起一個新的線程 * <請替換成功能描述> <br> * <請替換成詳細描述> * @author caizh * @since [1.0.0] * @version [1.0.0,2017年4月13日] */public class Demo01 extends Thread{ private String name; public Demo01(String name){ this.name = name; } public void run(){ for(int i=0;i<5;i++){ System.out.println(name+"運行"+i); try{ // Thread.sleep()方法調用目的是不讓當前線程獨自霸佔該進程所擷取的CPU資源, // 以留出一定時間給其他線程執行的機會。 sleep((int)Math.random()*10); }catch(InterruptedException e){ e.printStackTrace(); } } } public static void main(String[] args) { Demo01 mTh1 = new Demo01("A"); Demo01 mTh2 = new Demo01("B"); mTh1.start(); // 開啟線程 mTh2.start(); // start方法重複調用,會出現java.lang.IllegalThreadStateException異常 // 每次執行的結果都是隨機的 }}
輸出結果(每次的輸出結果都不同):
B運行0
A運行0
B運行1
B運行2
B運行3
B運行4
A運行1
A運行2
A運行3
A運行4
說明:程式啟動運行main時候,java虛擬機器啟動一個進程,主線程main和main()調用時候被建立。隨著調用 Demo01的兩個對象的start的方法,另外兩個線程也啟動了,這樣,整個應用就在多線程下運行。
注意:start()方法的調用後不是立即執行多線程代碼,而是使得該線程變為可運行態(Runnable),什麼時候運行是由作業系統決定的。
從程式啟動並執行結果可以發現,多線程程式是亂序執行的。因此只有亂序執行的代碼才有必要設計為多線程。
Thread.sleep()方法調用目的是為不讓當前線程獨自霸佔該進程所擷取的CPU資源,以留出一定時間給其他線程執行的機會。
實際上所有的線程代碼執行順序都是不確定的,每次執行的結果都是隨機的。但是start方法重複調用的話,會出現java.lang.IllegalThreadStateException異常。
Thread1 mTh1=new Demo01("A"); Thread1 mTh2=mTh1; mTh1.start(); mTh2.start();
輸出:
Exception in thread "main" java.lang.IllegalThreadStateException at java.lang.Thread.start(Unknown Source) at com.multithread.learning.Main.main(Main.java:31)A運行 : 0A運行 : 1A運行 : 2A運行 : 3A運行 : 4
二、實現java.lang.Runnable介面
採用Runnable也是非常常見的一種,我們只需要重寫run方法即可。下面也來看個執行個體。
package com.zhihua.Runnable;/** * 採用Runnable介面實現多線程(比較常用) * 實現run方法即可 * <請替換成功能描述> <br> * <請替換成詳細描述> * @author caizh * @since [1.0.0] * @version [1.0.0,2017年4月13日] */public class Demo01 implements Runnable{ private String name; public Demo01(String name){ this.name = name; } @Override public void run() { for(int i=0;i<5;i++){ System.out.println(name + "運行 : " + i); try { Thread.sleep((int) Math.random() * 10); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { /** * 在啟動多線程的時候,需要先通過Thread類的構造方法Thread(Runnable target) * 構造出對象,然後調用Thread對象的start()方法來運行多線程代碼。 */ new Thread(new Demo01("C")).start(); new Thread(new Demo01("D")).start(); }}
輸出:
C運行 : 0
D運行 : 0
C運行 : 1
D運行 : 1
C運行 : 2
D運行 : 2
C運行 : 3
D運行 : 3
C運行 : 4
D運行 : 4
說明:
該類通過實現Runnable介面,使得該類有了多線程的特徵。run()方法是多線程程式的一個約定。所有的多線程代碼都是在run方法裡面。Thread類實際上也是實現了Runnable介面的類。
在啟動的多線程的時候,需要先通過Thread類的構造方法Thread(Runnable target)構造出對象,然後調用Thread對象的start()方法來運行多線程代碼。
實際上所有的多線程代碼都是通過運行Thread的start()方法來啟動並執行。因此,不管是擴充Thread類還是實現Runnable介面來實現多線程,最終還是通過Thread的對象的API來控制線程的。熟悉Thread類的API是進行多線程編程的基礎。 三、Thread和Runnable的區別
如果一個類繼承Thread,則不適合資源共用。但是如果實現了Runable介面的話,則很容易的實現資源共用。
總結:
實現Runnable介面比繼承Thread類所具有的優勢:
1):適合多個相同的程式碼的線程去處理同一個資源
2):可以避免java中的單繼承的限制
3):增加程式的健壯性,代碼可以被多個線程共用,代碼和資料獨立
4):線程池只能放入實現Runable或callable類線程,不能直接放入繼承Thread的類
提醒一下大家:main方法其實也是一個線程。在 java中所以的線程都是同時啟動的,至於什麼時候,哪個先執行,完全看誰先得到CPU 的資源。
在 java 中,每次程式運行至少啟動2個線程。一個是main線程,一個是垃圾收集線程。因為每當使用java命令執行一個類的時候,實際上都會啟動一個JVM,每一個jVM實際上就是在作業系統中啟動了一個進程。 四、線程狀態轉換 下面的這個圖非常重要。你如果看懂了這個圖,那麼對於多線程的理解將會更加深刻。
1、建立狀態(New):新建立了一個線程對象。 2、就緒狀態(Runnable):線程對象建立後,其他線程調用了該對象的start()方法。該狀態的線程位於可運行線程池中,變得可運行,等待擷取CPU的使用權。 3、運行狀態(Running):就緒狀態的線程擷取了CPU,執行程式代碼。 4、阻塞狀態(Blocked):阻塞狀態是線程因為某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,才有機會轉到運行狀態。阻塞的情況分三種: (一)、等待阻塞:啟動並執行線程執行wait()方法,JVM會把該線程放入等待池中。(wait會釋放持有的鎖) (二)、同步阻塞:啟動並執行線程在擷取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM會把該線程放入鎖池中。 (三)、其他阻塞:啟動並執行線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置為阻塞狀態。當sleep()狀態逾時、join()等待線程終止或者逾時、或者I/O處理完畢時,線程重新轉入就緒狀態。(注意,sleep是不會釋放持有的鎖) 5、死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。
五、線程調度
線程的調度 1、調整線程優先順序:Java線程有優先順序,優先順序高的線程會獲得較多的運行機會。 Java線程的優先順序用整數表示,取值範圍是1~10,Thread類有以下三個靜態常量:
static int MAX_PRIORITY 線程可以具有的最高優先順序,取值為10。 static int MIN_PRIORITY 線程可以具有的最低優先順序,取值為1。 static int NORM_PRIORITY 分配給線程的預設優先順序,取值為5。
Thread類的setPriority()和getPriority()方法分別用來設定和擷取線程的優先順序。 每個線程都有預設的優先順序。主線程的預設優先順序為Thread.NORM_PRIORITY。 線程的優先順序有繼承關係,比如A線程中建立了B線程,那麼B將和A具有相同的優先順序。 JVM提供了10個線程優先順序,但與常見的作業系統都不能很好的映射。如果希望程式能移植到各個作業系統中,應該僅僅使用Thread類有以下三個靜態常量作為優先順序,這樣能保證同樣的優先順序採用了同樣的調度方式。 2、線程睡眠:Thread.sleep(long millis)方法,使線程轉到阻塞狀態。millis參數設定睡眠的時間,以毫秒為單位。當睡眠結束後,就轉為就緒(Runnable)狀態。sleep()平台移植性好。 3、線程等待:Object類中的wait()方法,導致當前的線程等待,直到其他線程調用此對象的 notify() 方法或 notifyAll() 喚醒方法。這個兩個喚醒方法也是Object類中的方法,行為等價於調用 wait(0) 一樣。 4、線程讓步:Thread.yield() 方法,暫停當前正在執行的線程對象,把執行機會讓給相同或者更高優先順序的線程。 5、線程加入:join()方法,等待其他線程終止。在當前線程中調用另一個線程的join()方法,則當前線程轉入阻塞狀態,直到另一個進程運行結束,當前線程再由阻塞轉為就緒狀態。 6、線程喚醒:Object類中的notify()方法,喚醒在此對象監視器上等待的單個線程。如果所有線程都在此對象上等待,則會選擇喚醒其中一個線程。選擇是任意性的,並在對實現做出決定時發生。線程通過調用其中一個 wait 方法,在對象的監視器上等待。 直到當前的線程放棄此對象上的鎖定,才能繼續執行被喚醒的線程。被喚醒的線程將以常規方式與在該對象上主動同步的其他所有線程進行競爭;例如,喚醒的線程在作為鎖定此對象的下一個線程方面沒有可靠的特權或劣勢。類似的方法還有一個notifyAll(),喚醒在此對象監視器上等待的所有線程。 注意:Thread中suspend()和resume()兩個方法在JDK1.5中已經廢除,不再介紹。因為有容易導致鎖死的。
六、常用函數說明
①sleep(long millis): 在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行)
②join():指等待t線程終止。
使用方式。
join是Thread類的一個方法,啟動線程後直接調用,即join()的作用是:“等待該線程終止”,這裡需要理解的就是該線程是指的主線程等待子線程的終止。也就是在子線程調用了join()方法後面的代碼,只有等到子線程結束了才能執行。
Thread t = new AThread(); t.start(); t.join();
為什麼要用join()方法
在很多情況下,主線程產生並起動了子線程,如果子線程裡要進行大量的耗時的運算,主線程往往將於子線程之前結束,但是如果主線程處理完其他的事務後,需要用到子線程的處理結果,也就是主線程需要等待子線程執行完成之後再結束,這個時候就要用到join()方法了。 不加join。
package com.zhihua.Thread;/** * join是Thread類的一個方法,啟動線程後直接調用,即join()的作用是:“等待該線程終止”, * 這裡需要理解的就是該線程是指的主線程等待子線程的終止。 * 也就是在子線程調用了join()方法後面的代碼,只有等到子線程結束了才能執行。 * <請替換成功能描述> <br> * <請替換成詳細描述> * * 為什麼要用join()方法: * 在很多情況下,主線程產生並起動了子線程,如果子線程裡要進行大量的耗時的運算, * 主線程往往將於子線程之前結束,但是如果主線程處理完其他的事務後,需要用到子線程的處理結果, * 也就是主線程需要等待子線程執行完成之後再結束,這個時候就要用到join()方法了。 * @author caizh * @since [1.0.0] * @version [1.0.0,2017年4月13日] */public class Thread_Join extends Thread{ private String name; public Thread_Join(String name){ super(name); this.name = name; } public void run(){ System.out.println(Thread.currentThread().getName()+" 線程運行開始。"); for (int i = 0; i < 5; i++) { System.out.println("子線程"+name + "運行 : " + i); try { sleep((int) Math.random() * 10); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + " 線程運行結束!"); } /* * 不加join */ public static void main(String[] args) { System.out.println(Thread.currentThread().getName()+" 主線程運行開始。"); Thread mTh1 = new Thread_Join("A"); Thread mTh2 = new Thread_Join("B"); mTh1.start(); mTh2.start(); System.out.println(Thread.currentThread().getName()+ "主線程運行結束!"); } }
輸出結果:main主線程運行開始!main主線程運行結束!B 線程運行開始!子線程B運行 : 0A 線程運行開始!子線程A運行 : 0子線程B運行 : 1子線程A運行 : 1子線程A運行 : 2子線程A運行 : 3子線程A運行 : 4A 線程運行結束!子線程B運行 : 2子線程B運行 : 3子線程B運行 : 4B 線程運行結束!發現主線程比子線程早結束
加join
package com.zhihua.Thread;/** * join是Thread類的一個方法,啟動線程後直接調用,即join()的作用是:“等待該線程終止”, * 這裡需要理解的就是該線程是指的主線程等待子線程的終止。 * 也就是在子線程調用了join()方法後面的代碼,只有等到子線程結束了才能執行。 * <請替換成功能描述> <br> * <請替換成詳細描述> * * 為什麼要用join()方法: * 在很多情況下,主線程產生並起動了子線程,如果子線程裡要進行大量的耗時的運算, * 主線程往往將於子線程之前結束,但是如果主線程處理完其他的事務後,需要用到子線程的處理結果, * 也就是主線程需要等待子線程執行完成之後再結束,這個時候就要用到join()方法了。 * @author caizh * @since [1.0.0] * @version [1.0.0,2017年4月13日] */public class Thread_Join extends Thread{ private String name; public Thread_Join(String name){ super(name); this.name = name; } public void run(){ System.out.println(Thread.currentThread().getName()+" 線程運行開始。"); for (int i = 0; i < 5; i++) { System.out.println("子線程"+name + "運行 : " + i); try { sleep((int) Math.random() * 10); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + " 線程運行結束!"); } /* * 加join */ public static void main(String[] args) { System.out.println(Thread.currentThread().getName()+"主線程運行開始!"); Thread mTh1 = new Thread_Join("A"); Thread mTh2 = new Thread_Join("B"); mTh1.start(); mTh2.start(); try { mTh1.join(); // main線程為主,mTh1、mTh2為子線程 } catch (Exception e) { } try { mTh2.join(); } catch (Exception e) { } System.out.println(Thread.currentThread().getName()+ "主線程運行結束!"); }}
運行結果:main主線程運行開始!A 線程運行開始!子線程A運行 : 0B 線程運行開始!子線程B運行 : 0子線程A運行 : 1子線程B運行 : 1子線程A運行 : 2子線程B運行 : 2子線程A運行 : 3子線程B運行 : 3子線程A運行 : 4子線程B運行 : 4A 線程運行結束!主線程一定會等子線程都結束了才結束
③yield():暫停當前正在執行的線程對象,並執行其他線程。
Thread.yield()方法作用是:暫停當前正在執行的線程對象,並執行其他線程。 yield()應該做的是讓當前運行線程回到可運行狀態,以允許具有相同優先順序的其他線程獲得運行機會。因此,使用yield()的目的是讓相同優先順序的線程之間能適當的輪轉執行。但是,實際中無法保證yield()達到讓步目的,因為讓步的線程還有可能被線程發送器再次選中。 結論:yield()從未導致線程轉到等待/睡眠/阻塞狀態。在大多數情況下,yield()將導致線程從運行狀態轉到可運行狀態,但有可能沒有效果。可看上面的圖。
sleep()和yield()的區別
sleep()和yield()的區別):sleep()使當前線程進入停滯狀態,所以執行sleep()的線程在指定的時間內肯定不會被執行;yield()只是使當前線程重新回到可執行狀態,所以執行yield()的線程有可能在進入到可執行狀態後馬上又被執行。
sleep 方法使當前運行中的線程睡眼一段時間,進入不可運行狀態,這段時間的長短是由程式設定的,yield 方法使當前線程讓出 CPU 佔有權,但讓出的時間是不可設定的。實際上,yield()方法對應了如下操作:先檢測當前是否有相同優先順序的線程處於同可運行狀態,如有,則把 CPU 的佔有權交給此線程,否則,繼續運行原來的線程。所以yield()方法稱為“退讓”,它把運行機會讓給了同等優先順序的其他線程
另外,sleep 方法允許較低優先順序的線程獲得運行機會,但 yield() 方法執行時,當前線程仍處在可運行狀態,所以,不可能讓出較低優先順序的線程些時獲得 CPU 佔有權。在一個運行系統中,如果較高優先順序的線程沒有調用 sleep 方法,又沒有受到 I\O 阻塞,那麼,較低優先順序線程只能等待所有較高優先順序的線程運行結束,才有機會運行。
④setPriority(): 更改線程的優先順序。
MIN_PRIORITY = 1
NORM_PRIORITY = 5
MAX_PRIORITY = 10用法:
Thread4 t1 = new Thread4("t1");Thread4 t2 = new Thread4("t2");t1.setPriority(Thread.MAX_PRIORITY);t2.setPriority(Thread.MIN_PRIORITY);
⑤interrupt():不要以為它是中斷某個線程。它只是線線程發送一個中斷訊號,讓線程在無限等待時(如死結時)能拋出拋出,從而結束線程,但是如果你吃掉了這個異常,那麼這個線程還是不會中斷的。
⑥wait()
Obj.wait(),與Obj.notify()必須要與synchronized(Obj)一起使用,也就是wait,與notify是針對已經擷取了Obj鎖進行操作,從文法角度來說就是Obj.wait(),Obj.notify必須在synchronized(Obj){…}語句塊內。從功能上來說wait就是說線程在擷取對象鎖後,主動釋放對象鎖,同時本線程休眠。直到有其它線程調用對象的notify()喚醒該線程,才能繼續擷取對象鎖,並繼續執行。相應的notify()就是對對象鎖的喚醒操作。但有一點需要注意的是notify()調用後,並不是馬上就釋放對象鎖的,而是在相應的synchronized(){}語句塊執行結束,自動釋放鎖後,JVM會在wait()對象鎖的線程中隨機選取一線程,賦予其對象鎖,喚醒線程,繼續執行。這樣就提供了線上程間同步、喚醒的操作。Thread.sleep()與Object.wait()二者都可以暫停當前線程,釋放CPU控制權,主要的區別在於Object.wait()在釋放CPU同時,釋放了對象鎖的控制。
單單在概念上理解清楚了還不夠,需要在實際的例子中進行測試才能更好的理解。對Object.wait(),Object.notify()的應用最經典的例子,應該是三線程列印ABC的問題了吧,這是一道比較經典的面試題,題目要求如下:
建立三個線程,A線程列印10次A,B線程列印10次B,C線程列印10次C,要求線程同時運行,交替列印10次ABC。這個問題用Object的wait(),notify()就可以很方便的解決。代碼如下:
package com.zhihua.Thread_Wait;/** * 這是一道比較經典的面試題,題目要求如下: * 建立三個線程,A線程列印10次A,B線程列印10次B,C線程列印10次C,要求線程同時運行,交替列印10次ABC。 * 這個問題用Object的wait(),notify()就可以很方便的解決。 * <請替換成功能描述> <br> * <請替換成詳細描述> * @author caizh * @since [1.0.0] * @version [1.0.0,2017年4月13日] */public class MyThreadPrinter implements Runnable{ private String name; private Object prev; private Object self; private MyThreadPrinter(String name,Object prev,Object self){ this.name = name; this.prev = prev; this.self = self; } @Override public void run() { int count = 10; while(count>0){ synchronized(prev){ synchronized(self){ System.out.print(name); count--; self.notify(); } try{ prev.wait(); }catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) throws InterruptedException { Object a = new Object(); Object b = new Object(); Object c = new Object(); MyThreadPrinter pa = new MyThreadPrinter("A",c,a); MyThreadPrinter pb = new MyThreadPrinter("B",a,b); MyThreadPrinter pc = new MyThreadPrinter("C",b,c); new Thread(pa).start(); Thread.sleep(100); // 確保按順序A、B、C執行 new Thread(pb).start(); Thread.sleep(100); new Thread(pc).start(); Thread.sleep(100); }}
輸出結果:
ABCABCABCABCABCABCABCABCABCABC
先來解釋一下其整體思路,從大的方向上來講,該問題為三線程間的同步喚醒操作,主要的目的就是ThreadA->ThreadB->ThreadC->ThreadA迴圈執行三個線程。為了控制線程執行的順序,那麼就必須要確定喚醒、等待的順序,所以每一個線程必須同時持有兩個對象鎖,才能繼續執行。一個對象鎖是prev,就是前一個線程所持有的對象鎖。還有一個就是自身對象鎖。主要的思想就是,為了控制執行的順序,必須要先持有prev鎖,也就前一個線程要釋放自身對象鎖,再去申請自身對象鎖,兩者兼備時列印,之後首先調用self.notify()釋放自身對象鎖,喚醒下一個等待線程,再調用prev.wait()釋放prev對象鎖,終止當前線程,等待迴圈結束後再次被喚醒。運行上述代碼,可以發現三個線程迴圈列印ABC,共10次。程式啟動並執行主要過程就是A線程最先運行,持有C,A對象鎖,後釋放A,C鎖,喚醒B。線程B等待A鎖,再申請B鎖,後列印B,再釋放B,A鎖,喚醒C,線程C等待B鎖,再申請C鎖,後列印C,再釋放C,B鎖,喚醒A。看起來似乎沒什麼問題,但如果你仔細想一下,就會發現有問題,就是初始條件,三個線程按照A,B,C的順序來啟動,按照前面的思考,A喚醒B,B喚醒C,C再喚醒A。但是這種假設依賴於JVM中線程調度、執行的順序。
wait和sleep區別
共同點:
1. 他們都是在多線程的環境下,都可以在程式的調用處阻塞指定的毫秒數,並返回。
2. wait()和sleep()都可以通過interrupt()方法 打斷線程的暫停狀態 ,從而使線程立刻拋出InterruptedException。
如果線程A希望立即結束線程B,則可以對線程B對應的Thread執行個體調用interrupt方法。如果此刻線程B正在wait/sleep /join,則線程B會立刻拋出InterruptedException,在catch() {} 中直接return即可安全地結束線程。
需要注意的是,InterruptedException是線程自己從內部拋出的,並不是interrupt()方法拋出的。對某一線程調用 interrupt()時,如果該線程正在執行普通的代碼,那麼該線程根本就不會拋出InterruptedException。但是,一旦該線程進入到 wait()/sleep()/join()後,就會立刻拋出InterruptedException 。
不同點:
1. Thread類的方法:sleep(),yield()等
Object的方法:wait()和notify()等
2. 每個對象都有一個鎖來控制同步訪問。Synchronized關鍵字可以和對象的鎖互動,來實現線程的同步。
sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法。
3. wait,notify和notifyAll只能在同步控制方法或者同步控制塊裡面使用,而sleep可以在任何地方使用
所以sleep()和wait()方法的最大區別是:
sleep()睡眠時,保持對象鎖,仍然佔有該鎖;
而wait()睡眠時,釋放對象鎖。
但是wait()和sleep()都可以通過interrupt()方法打斷線程的暫停狀態,從而使線程立刻拋出InterruptedException(但不建議使用該方法)。
sleep()方法
sleep()使當前線程進入停滯狀態(阻塞當前線程),讓出CUP的使用、目的是不讓當前線程獨自霸佔該進程所獲的CPU資源,以留一定時間給其他線程執行的機會;
sleep()是Thread類的Static(靜態)的方法;因此他不能改變對象的機鎖,所以當在一個Synchronized塊中調用Sleep()方法是,線程雖然休眠了,但是對象的機鎖並木有被釋放,其他線程無法訪問這個對象(即使睡著也持有對象鎖)。
在sleep()休眠時間期滿後,該線程不一定會立即執行,這是因為其它線程可能正在運行而且沒有被調度為放棄執行,除非此線程具有更高的優先順序。
wait()方法
wait()方法是Object類裡的方法;當一個線程執行到wait()方法時,它就進入到一個和該對象相關的等待池中,同時失去(釋放)了對象的機鎖(暫時失去機鎖,wait(long timeout)逾時時間到後還需要返還對象鎖);其他線程可以訪問;
wait()使用notify或者notifyAlll或者指定睡眠時間來喚醒當前等待池中的線程。
wiat()必須放在synchronized block中,否則會在program runtime時扔出”java.lang.IllegalMonitorStateException“異常。
七、常見線程名詞解釋 主線程:JVM調用程式main()所產生的線程。 當前線程:這個是容易混淆的概念。一般指通過Thread.cur