第1章 Java多線程技能
- 在Java中有以下3種方法可以終止正在運(yùn)行的線程:
1)使用退出標(biāo)志,使線程正常退出,也就是當(dāng)run方法完成后線程終止。
2)使用stop方法強(qiáng)行終止線程,但是不推薦使用這個(gè)方法,因?yàn)閟top和suspend及resume一樣,都是作廢過(guò)期的方法,使用它們可能產(chǎn)生不可預(yù)料的結(jié)果。
3)使用interrupt方法中斷線程。
調(diào)用interrupt()方法僅僅是在當(dāng)前線程中打了一個(gè)停止的標(biāo)記,并不是真的停止線程。 - 判斷線程是否是停止?fàn)顟B(tài)
1)this.interrupted():測(cè)試當(dāng)前線程是否已經(jīng)中斷。
2)this.isInterrupted():測(cè)試線程能夠是否已經(jīng)中斷。
interrupted()方法具有清除狀態(tài)的功能。 - yield方法
yield()方法的作用是放棄當(dāng)前的CPU資源,將他讓給其他的任務(wù)去占用CPU執(zhí)行時(shí)間。但放棄的時(shí)間不確定,有可能剛剛放棄,馬上又獲得CPU時(shí)間片。 - 線程的優(yōu)先級(jí)
在Java中,線程的優(yōu)先級(jí)具有繼承性,比如A線程啟動(dòng)B線程,則B線程的優(yōu)先級(jí)與A是一樣的。
第2章 對(duì)象及變量的并發(fā)訪問(wèn)
2.1 synchronized同步方法
- 方法內(nèi)的變量為線程安全,這是方法內(nèi)部的變量是私有的特性造成的。
- 實(shí)例變量非線程安全。
- 在兩個(gè)線程訪問(wèn)同一個(gè)對(duì)象中的同步方法時(shí)一定是線程安全的。
- 關(guān)鍵字synchronized取得的鎖都是對(duì)象鎖,而不是把一段代碼或方法當(dāng)作鎖。
- 調(diào)用用關(guān)鍵字synchronized聲明的方法一定是排隊(duì)運(yùn)行的。另外需要牢牢記住“共享”這兩個(gè)字,只有共享資源的讀寫訪問(wèn)才需要同步化,如果不是共享資源,那么根本就沒有同步的必要。
- 當(dāng)A線程調(diào)用anyObject對(duì)象加入synchronized關(guān)鍵字的X方法時(shí),A線程就獲得了X方法鎖,更準(zhǔn)確地講,是獲得了對(duì)象的鎖,所以其他線程必須等A線程執(zhí)行完畢才可以調(diào)用X方法,但B線程可以隨意調(diào)用其他的非synchronized同步方法。當(dāng)A線程調(diào)用anyObject對(duì)象加入synchronized關(guān)鍵字的X方法時(shí),A線程就獲得了X方法所在對(duì)象的鎖,所以其他線程必須等A線程執(zhí)行完畢才可以調(diào)用X方法,而B線程如果調(diào)用了synchronized關(guān)鍵字的非X方法時(shí),必須等A線程將X方法執(zhí)行完,也就是釋放對(duì)象鎖后才可以調(diào)用。這時(shí)A線程已經(jīng)執(zhí)行了一個(gè)完整的任務(wù),也就是說(shuō)username和password這兩個(gè)實(shí)例變量已經(jīng)同時(shí)被賦值,不存在臟讀的基本環(huán)境。
- synchronized鎖重入:關(guān)鍵字synchronized擁有鎖重入的功能,也就是在使用synchronized時(shí),當(dāng)一個(gè)線程得到一個(gè)對(duì)象鎖后,再次請(qǐng)求此對(duì)象鎖時(shí)是可以再次得到該對(duì)象的鎖的。
- 當(dāng)一個(gè)線程執(zhí)行的代碼出現(xiàn)異常時(shí),其所持有的鎖會(huì)自動(dòng)釋放。
- 同步不可以繼承。
2.2 synchronized同步語(yǔ)句塊
用關(guān)鍵字synchronized聲明方法在某些情況下是有弊端的,比如A線程調(diào)用同步方法執(zhí)行一個(gè)長(zhǎng)時(shí)間的任務(wù),那么B線程則必須等待比較長(zhǎng)時(shí)間。在這樣的情況下可以使用synchronized同步語(yǔ)句塊來(lái)解決。
在使用同步synchronized(this)代碼塊時(shí)需要注意的是,當(dāng)一個(gè)線程訪問(wèn)object的一個(gè)synchronized(this)同步代碼塊時(shí),其他線程對(duì)同一個(gè)object中所有其他synchronized(this)同步代碼塊的訪問(wèn)將被阻塞,這說(shuō)明synchronized使用的“對(duì)象監(jiān)視器”是一個(gè)。
和synchronized方法一樣,synchronized(this)代碼塊也是鎖定當(dāng)前對(duì)象的。
多個(gè)線程調(diào)用同一個(gè)對(duì)象中的不同名稱的synchronized同步方法或synchronized(this)同步代碼塊時(shí),調(diào)用的效果就是按順序執(zhí)行,也就是同步的,阻塞的。
這說(shuō)明synchronized同步方法或synchronized(this)同步代碼塊分別有兩種作用。
(1)synchronized同步方法
1)對(duì)其他synchronized同步方法或synchronized(this)同步代碼塊調(diào)用呈阻塞狀態(tài)。
2)同一時(shí)間只有一個(gè)線程可以執(zhí)行synchronized同步方法中的代碼。
(2)synchronized(this)同步代碼塊
1)對(duì)其他synchronized同步方法或synchronized(this)同步代碼塊調(diào)用呈阻塞狀態(tài)。
2)同一時(shí)間只有一個(gè)線程可以執(zhí)行synchronized同步代碼塊中的代碼。
如果在一個(gè)類中有很多個(gè)synchronized方法,這時(shí)雖然能實(shí)現(xiàn)同步,但會(huì)受到阻塞,所以影響運(yùn)行效率;但如果使用同步代碼塊鎖非this對(duì)象,則synchronized(非this)代碼塊中的程序與同步方法是異步的,不與其他鎖this同步方法爭(zhēng)搶this鎖,則可大大提高運(yùn)行效率。
使用“synchronized(非this對(duì)象x)同步代碼塊”格式進(jìn)行同步操作時(shí),對(duì)象監(jiān)視器必須是同一個(gè)對(duì)象。如果不是同一個(gè)對(duì)象監(jiān)視器,運(yùn)行的結(jié)果就是異步調(diào)用了,就會(huì)交叉運(yùn)行。
1)當(dāng)多個(gè)線程同時(shí)執(zhí)行synchronized(X){}同步代碼塊時(shí)呈同步效果。
2)當(dāng)其他線程執(zhí)行x對(duì)象中synchronized同步方法時(shí)呈同步效果。
3)當(dāng)其他線程執(zhí)行x對(duì)象方法里面的synchronized(this)代碼塊時(shí)也呈現(xiàn)同步效果。
但需要注意:如果其他線程調(diào)用不加synchronized關(guān)鍵字的方法時(shí),還是異步調(diào)用。
關(guān)鍵字volatile的主要作用是使變量在多個(gè)線程間可見。
關(guān)鍵字volatile的作用是強(qiáng)制從公共堆棧中取得變量的值,而不是從線程私有數(shù)據(jù)棧中取得變量的值。
synchronized和volatile比較:
- 關(guān)鍵字volatile是線程同步的輕量級(jí)實(shí)現(xiàn),所以volatile性能肯定比synchronized要好,并且volatile只能修飾于變量,而synchronized可以修飾方法,以及代碼塊。
- 多線程訪問(wèn)volatile不會(huì)發(fā)生阻塞,而synchronized會(huì)出現(xiàn)阻塞。
- volatile能保證數(shù)據(jù)的可見性,但不能保證原子性;而synchronized可以保證原子性,也可以間接保證可見性,因?yàn)樗鼤?huì)將私有內(nèi)存和公共內(nèi)存中的數(shù)據(jù)做同步。
- 關(guān)鍵字volatile解決的是變量在多個(gè)線程之間的可見性;而synchronized關(guān)鍵字解決的是多個(gè)線程之間訪問(wèn)資源的同步性。
第3章 線程間通信
方法wait()的作用是使當(dāng)前執(zhí)行代碼的線程進(jìn)行等待,wait()方法是Object類的方法,該方法用來(lái)將當(dāng)前線程置入“預(yù)執(zhí)行隊(duì)列”中,并且在wait()所在的代碼行處停止執(zhí)行,直到接到通知或被中斷為止。在調(diào)用wait()之前,線程必須獲得該對(duì)象的對(duì)象級(jí)別鎖,即只能在同步方法或同步塊中調(diào)用wait()方法。在執(zhí)行wait()方法后,當(dāng)前線程釋放鎖。在從wait()返回前,線程與其他線程競(jìng)爭(zhēng)重新獲得鎖。如果調(diào)用wait()時(shí)沒有持有適當(dāng)?shù)逆i,則拋出IllegalMonitorStateException,它是RuntimeException的一個(gè)子類,因此,不需要try-catch語(yǔ)句進(jìn)行捕獲異常。
方法notify()也要在同步方法或同步塊中調(diào)用,即在調(diào)用前,線程也必須獲得該對(duì)象的對(duì)象級(jí)別鎖。如果調(diào)用notify()時(shí)滅有持有適當(dāng)?shù)逆i,也會(huì)拋出IllegalMonitorStateException。該方法用來(lái)通知那些可能等待該對(duì)象的對(duì)象鎖的其他線程,對(duì)其發(fā)出通知notify,并使它等待獲取該對(duì)象的對(duì)象鎖。需要說(shuō)明的是,在執(zhí)行notify()方法后,當(dāng)前線程不會(huì)馬上釋放該對(duì)象鎖,呈wait狀態(tài)的線程也并不能馬上獲取該對(duì)象鎖,要等到執(zhí)行notify()方法的線程將程序執(zhí)行完,也就是退出synchronized代碼塊后,當(dāng)前線程才會(huì)釋放鎖,而呈wait狀態(tài)所在的線程才可以獲取該對(duì)象鎖。當(dāng)?shù)谝粋€(gè)獲得了該對(duì)象鎖的wait線程運(yùn)行完畢以后,它會(huì)釋放掉該對(duì)象鎖,此時(shí)如果該對(duì)象沒有再次使用notify語(yǔ)句,則即便該對(duì)象已經(jīng)空閑,其他wait狀態(tài)等待的線程由于沒有得到該對(duì)象的通知,還會(huì)繼續(xù)阻塞在wait狀態(tài),直到這個(gè)對(duì)象發(fā)出一個(gè)notify或nofityAll。
用一句話來(lái)總結(jié)一下wait和notify:wait使線程停止運(yùn)行,而notify使停止的線程繼續(xù)運(yùn)行。
關(guān)鍵字synchronized可以將任何一個(gè)Object對(duì)象作為同步對(duì)象來(lái)看待,而Java為每個(gè)Object都實(shí)現(xiàn)了wait()和notify()方法,它們必須用在被synchronized同步的Object的臨界區(qū)內(nèi)。通過(guò)調(diào)用wait()方法可以使處于臨界區(qū)內(nèi)的線程進(jìn)入等待狀態(tài),同時(shí)釋放被同步對(duì)象的鎖。而notify操作可以喚醒一個(gè)因調(diào)用了wait操作而處于阻塞狀態(tài)中的線程,使其進(jìn)入就緒狀態(tài)。被重新喚醒的線程會(huì)試圖重新獲得臨界區(qū)的控制權(quán),也就是鎖,并繼續(xù)執(zhí)行臨界區(qū)內(nèi)wait之后的代碼。如果發(fā)出notify操作時(shí)沒有處于阻塞狀態(tài)中的線程,那么該命令會(huì)被忽略。
wait()方法可以使調(diào)用該方法的線程釋放共享資源的鎖,然后從運(yùn)行狀態(tài)退出,進(jìn)入等待隊(duì)列,直到被再次喚醒。
notify()方法可以隨機(jī)喚醒等待隊(duì)列中等待同一共享資源的“一個(gè)”線程,并使該線程退出等待隊(duì)列,進(jìn)入可運(yùn)行狀態(tài),也就是notify()方法僅通知“一個(gè)”線程。
notifyAll()方法可以使所有正在等待隊(duì)列中等待同一共享資源的“全部”線程從等待狀態(tài)退出,進(jìn)入可運(yùn)行狀態(tài)。此時(shí),優(yōu)先級(jí)最高的那個(gè)線程最先執(zhí)行,但也有可能是隨機(jī)執(zhí)行,因?yàn)檫@要取決于JVM虛擬機(jī)的實(shí)現(xiàn)。
當(dāng)方法wait()被執(zhí)行后,鎖被自動(dòng)釋放,但執(zhí)行完notify()方法,鎖卻不自動(dòng)釋放。
帶一個(gè)參數(shù)的wait(long)方法的功能是等待某一時(shí)間內(nèi)是否有線程對(duì)鎖進(jìn)行喚醒,如果超過(guò)這個(gè)時(shí)間則自動(dòng)喚醒。