一隆圆、線程分類
二、線程的實現(xiàn)方式
三翔烁、線程的狀態(tài)
四、線程的核心方法
sleep()方法:該方法屬于Thread類旨涝,是線程用來控制自身流程的蹬屹,在線程休眠的過程中不會釋放所持有的對象鎖。
在指定的毫秒數(shù)加指定的納秒數(shù)內(nèi)讓當前正在執(zhí)行的線程休眠(暫停執(zhí)行)白华,此操作受到系統(tǒng)計時器和調(diào)度程序精度和準確性的影響慨默。該線程不丟失任何監(jiān)視器的所屬權;wait()方法:該方法屬于Object類且為final方法弧腥,無法被子類覆寫厦取,用來線程間的通信,線程會放棄對象鎖管搪,進入等待此對象的等待鎖定池虾攻,只有針對此對象調(diào)用notify()方法后本線程才進入對象鎖定池準備獲取對象鎖進入運行狀態(tài)铡买。此方法一般與notify()或notifyAll()方法配套使用。
notify()方法:該方法屬于Object類且為final方法霎箍,無法被子類覆寫奇钞,用來線程間通信。通常由當前對象調(diào)用該方法形如Object.notify()喚醒一個正在等待該對象的monitor(鎖)的線程漂坏,當有多個線程都在等待該對象的monitor(鎖)的話景埃,則只能喚醒其中一個線程,具體喚醒哪個線程則不得而知顶别。調(diào)用notify()方法必須在同步塊或者同步方法中進行(synchronized塊或者synchronized方法)谷徙。
notifyAll()方法:該方法屬于Object類且為final方法,無法被子類覆寫驯绎,用來線程間通信完慧。notifyAll()方法能夠喚醒所有正在等待該對象的monitor(鎖)的線程,這一點與notify()方法是不同的条篷。notify()和notifyAll()方法只是喚醒等待該對象的monitor(鎖)的線程骗随,并不決定哪個線程能夠獲取到monitor(鎖)。你可以用 notify 和 notifyAll 來通知那些在此監(jiān)視器上等待的線程重新開始運行赴叹。不同之處在于鸿染,notify 僅僅通知一個線程,并且我們不知道哪個線程會收到通知乞巧,然而 notifyAll 會通知所有等待中的線程涨椒。換言之,如果只有一個線程在等待一個信號燈绽媒,notify和notifyAll都會通知到這個線程蚕冬。但如果多個線程在等待這個信 號燈,那么notify只會通知到其中一個是辕,而其它線程并不會收到任何通知囤热,而notifyAll會喚醒所有等待中的線程。wait()會使持有該對象鎖的線程釋放占有的鎖,notify和notifyAll不會使持有該對象鎖的線程釋放占用的鎖获三。
join()方法:join是Thread類的一個方法旁蔼,啟動線程后直接調(diào)用,例如:
Thread t = new AThread(); t.start(); t.join();
在很多情況下疙教,主線程生成并起動了子線程棺聊,如果子線程里要進行大量的耗時的運算,主線程往往將于子線程之前結束贞谓,但是如果主線程處理完其他的事務后限佩,需要用到子線程的處理結果,也就是主線程需要等待子線程執(zhí)行完成之后再結束,這個時候就要用到join()方法了祟同。
即join()的作用是:“等待該線程終止”作喘,這里需要理解的就是該線程是指的主線程等待子線程的終止。也就是在子線程調(diào)用了join()方法后面的代碼耐亏,只有等到子線程結束了才能執(zhí)行徊都。
- interrupt()方法:
interrupt()并不直接中斷線程,而是設定一個中斷標識广辰,然后由程序進行中斷檢查暇矫,確定是否中斷。
- sleep() &interrupt()
線程A正在使用sleep()暫停著:Thread.sleep(100000);
如果要取消他的等待狀態(tài),可以在正在執(zhí)行的線程里(比如這里是B)調(diào)用a.interrupt();
令線程A放棄睡眠操作,這里a是線程A對應到的Thread實例執(zhí)行interrupt()時,并不需要獲取Thread實例的鎖定.任何線程在任何時刻,都可以調(diào)用其他線程interrupt().當sleep中的線程被調(diào)用interrupt()時,就會放棄暫停的狀態(tài).并拋出InterruptedException.丟出異常的,是A線程. - wait() & interrupt()
線程A調(diào)用了wait()進入了等待狀態(tài),也可以用interrupt()取消.
不過這時候要小心鎖定的問題.線程在進入等待區(qū),會把鎖定解除,當對等待中的線程調(diào)用interrupt()時(注意是等待的線程調(diào)用其自己的interrupt()),會先重新獲取鎖定,再拋出異常.在獲取鎖定之前,是無法拋出異常的. - join() & interrupt()
當線程以join()等待其他線程結束時,一樣可以使用interrupt()取消之.因為調(diào)用join()不需要獲取鎖定,
故與sleep()時一樣,會馬上跳到catch塊里.注意是隨調(diào)用interrupt()方法,一定是阻塞的線程來調(diào)用其自己的interrupt方法.如在線程a中調(diào)用來線程t.join().則a會等t執(zhí)行完后在執(zhí)行t.join后的代碼,當在線程b中調(diào)用來a.interrupt()方法,則會拋出InterruptedException择吊。 - interrupt()只是改變中斷狀態(tài)而已
interrupt()不會中斷一個正在運行的線程李根。這一方法實際上完成的是,在線程受到阻塞時拋出一個中斷信號几睛,這樣線程就得以退出阻塞的狀態(tài)房轿。更確切的說,如果線程被Object.wait,Thread.join和Thread.sleep三種方法之一阻塞所森,那么囱持,它將接收到一個中斷異常(InterruptedException),從而提早地終結被阻塞狀態(tài)焕济。
如果線程沒有被阻塞纷妆,這時調(diào)用interrupt()將不起作用;否則,線程就將得到異常(該線程必須事先預備好處理此狀況)晴弃,接著逃離阻塞狀態(tài)掩幢。
線程A在執(zhí)行sleep,wait,join時,線程B調(diào)用A的interrupt方法,的確這一個時候A會有InterruptedException異常拋出來.但這其實是在sleep,wait,join這些方法內(nèi)部會不斷檢查中斷狀態(tài)的值,而自己拋出的InterruptedException。
如果線程A正在執(zhí)行一些指定的操作時如賦值,for,while,if,調(diào)用方法等,都不會去檢查中斷狀態(tài),所以線程A不會拋出InterruptedException,而會一直執(zhí)行著自己的操作.當線程A終于執(zhí)行到wait(),sleep(),join()時,才馬上會拋出InterruptedException.
若沒有調(diào)用sleep(),wait(),join()這些方法,或是沒有在線程里自己檢查中斷狀態(tài)自己拋出InterruptedException的話,那InterruptedException是不會被拋出來的.
五上鞠、wait()方法和sleep()方法的區(qū)別
相同點:
- 通過Thread.sleep()和Object.wait()方法都可以暫停當前的線程际邻,對于CPU資源來說,不管是哪種方式暫停的線程芍阎,都表示它暫時不再需要CPU的執(zhí)行時間世曾。OS會將執(zhí)行時間分配給其它線程。
不同點:
- sleep()是Thread類中定義的方法,wait()是Object類中定義的方法;
- 調(diào)用Thread.sleep()方法的線程不會釋放該線程所擁有的鎖谴咸,當前對象調(diào)用Object.wait()方法后持有該對象鎖的線程會釋放所持有的對象鎖度硝。
- 當前對象調(diào)用Object.wait()方法后需要別的線程執(zhí)行notify()/notifyAll()才能夠有機會重新獲得CPU執(zhí)行時間。
六寿冕、wait(),notify(),notifyAll()
- 你可以使用wait和notify函數(shù)來實現(xiàn)線程間通信。你可以用它們來實現(xiàn)多線程(>3)之間的通信椒袍。
- 永遠在synchronized的函數(shù)或?qū)ο罄锸褂脀ait驼唱、notify和notifyAll,不然Java虛擬機會生成 IllegalMonitorStateException驹暑。
- 永遠在while循環(huán)里而不是if語句下使用wait玫恳。這樣辨赐,循環(huán)會在線程睡眠前后都檢查wait的條件,并在條件實際上并未改變的情況下處理喚醒通知京办。
- 永遠在多線程間共享的對象(在生產(chǎn)者消費者模型里即緩沖區(qū)隊列)上使用wait掀序。
- 相較于notify()方法,多使用notifyAll()方法惭婿。
六不恭、代碼示例
假設線程A實現(xiàn)了Runnable接口,其run方法如下:
@Override
public void run(){
synchronized(obj) {
while(!condition) {
obj.wait();
}
obj.doSomething();
}
}
當線程A獲得了obj鎖后,發(fā)現(xiàn)條件condition不滿足财饥,無法繼續(xù)下一處理换吧,于是線程A就wait() , 放棄對象鎖.交由系統(tǒng)進行線程的調(diào)度。
假設線程B也實現(xiàn)了Runnable接口,其run方法如下:
@Override
public void run(){
synchronized(obj) {
condition = true;
obj.notify();
}
}
如果B持有了obj對象鎖之后钥星,使得線程A的condition條件滿足了沾瓦,就可以喚醒等待obj對象鎖的線程A:
幾點說明:
- 調(diào)用obj的wait(), notify()方法前,必須獲得obj鎖谦炒,也就是必須寫在synchronized(obj) {…} 代碼段內(nèi)贯莺。
- 調(diào)用obj.wait()后,線程A就釋放了obj的鎖宁改,否則線程B無法獲得obj鎖缕探,也就無法在synchronized(obj) {…} 代碼段內(nèi)喚醒等待obj對象鎖的線程A。
- 當obj.wait()方法返回后透且,線程A需要再次獲得obj鎖撕蔼,才能繼續(xù)執(zhí)行。
- 如果A1,A2,A3都在obj.wait()秽誊,則B調(diào)用obj.notify()只能喚醒A1,A2,A3中的一個(具體哪一個由JVM決定)鲸沮。
- obj.notifyAll()則能全部喚醒A1,A2,A3,但是要繼續(xù)執(zhí)行obj.wait()的下一條語句锅论,必須獲得obj鎖讼溺,因此,A1,A2,A3只有一個有機會獲得鎖繼續(xù)執(zhí)行最易,例如A1怒坯,其余的需要等待A1釋放obj鎖之后才能繼續(xù)執(zhí)行。
- 當B調(diào)用obj.notify/notifyAll的時候藻懒,B正持有obj鎖剔猿,因此,A1,A2,A3雖被喚醒嬉荆,但是仍無法獲得obj鎖归敬。直到B退出synchronized塊,釋放obj鎖后,A1,A2,A3中的一個才有機會獲得鎖繼續(xù)執(zhí)行汪茧。