讓我們繼續(xù)前面有關(guān)ReentrantLock的話題。
首先,ReentrantLock有一個(gè)帶布爾型參數(shù)的構(gòu)造函數(shù),在JDK官方文檔中對(duì)它是這樣描述的:
“此類(lèi)的構(gòu)造方法接受一個(gè)可選的公平 參數(shù)。當(dāng)設(shè)置為 true 時(shí),在多個(gè)線程的爭(zhēng)用下,這些鎖傾向于將訪問(wèn)權(quán)授予等待時(shí)間最長(zhǎng)的線程。否則此鎖將無(wú)法保證任何特定訪問(wèn)順序。與采用默認(rèn)設(shè)置(使用不公平鎖)相比,使用公平鎖的程序在許多線程訪問(wèn)時(shí)表現(xiàn)為很低的總體吞吐量(即速度很慢,常常極其慢),但是在獲得鎖和保證鎖分配的均衡性時(shí)差異較小。不過(guò)要注意的是,公平鎖不能保證線程調(diào)度的公平性。因此,使用公平鎖的眾多線程中的一員可能獲得多倍的成功機(jī)會(huì),這種情況發(fā)生在其他活動(dòng)線程沒(méi)有被處理并且目前并未持有鎖時(shí)。還要注意的是,未定時(shí)的 tryLock  方法并沒(méi)有使用公平設(shè)置。因?yàn)榧词蛊渌€程正在等待,只要該鎖是可用的,此方法就可以獲得成功。 ”

簡(jiǎn)單來(lái)講:公平鎖使線程按照請(qǐng)求鎖的順序依次獲得鎖;而不公平鎖則允許討價(jià)還價(jià),在這種情況下,線程有時(shí)可以比先請(qǐng)求鎖的其他線程先得到鎖。

觀察采用公平鎖和非公平鎖的例程運(yùn)行效果發(fā)現(xiàn):線程獲得鎖的順序發(fā)生了一些變化(見(jiàn)下表)。

Unfair:


1 is running!

1 got lock1@Step1!

3 is running!

2 is running!

1 first Reading count:1

1 release lock1@Step1!

3 got lock1@Step1!

1 got lock2@Step2!

thread 1 set age to:18

thread 1 first read age is:18

3 first Reading count:2

3 release lock1@Step1!

2 got lock1@Step1!

thread 1 second read age is:18

1 release lock2@Step2!

3 got lock2@Step2!

thread 3 set age to:34

thread 3 first read age is:34

2 first Reading count:3

2 release lock1@Step1!

thread 3 second read age is:34

3 release lock2@Step2!

2 got lock2@Step2!

thread 2 set age to:72

thread 2 first read age is:72

thread 2 second read age is:72

2 release lock2@Step2!

成功生成(總時(shí)間:20 秒)

Fair:


1 is running!

1 got lock1@Step1!

2 is running!

3 is running!

1 first Reading count:1

1 release lock1@Step1!

1 got lock2@Step2!

thread 1 set age to:82

thread 1 first read age is:82

2 got lock1@Step1!

2 first Reading count:2

2 release lock1@Step1!

3 got lock1@Step1!

thread 1 second read age is:82

1 release lock2@Step2!

2 got lock2@Step2!

thread 2 set age to:65

thread 2 first read age is:65

3 first Reading count:3

3 release lock1@Step1!

thread 2 second read age is:65

2 release lock2@Step2!

3 got lock2@Step2!

thread 3 set age to:31

thread 3 first read age is:31

thread 3 second read age is:31

3 release lock2@Step2!

成功生成(總時(shí)間:20 秒)

轉(zhuǎn)載注明出處:http://x- spirit.javaeye.com/、http: //www.aygfsteel.com/zhangwei217245/
這樣的變化告訴我們:
采用非公平的鎖時(shí),當(dāng)一個(gè)線程釋放了第一個(gè)鎖以后,由于線程的搶占,剛剛被釋放的鎖馬上被下一個(gè)線程占有。采用公平鎖時(shí),由于公平鎖傾向于將訪問(wèn)權(quán)授予等待時(shí)間最長(zhǎng)的線程,所以,當(dāng)?shù)谝粋€(gè)鎖被第一個(gè)線程釋放以后,第二個(gè)鎖馬上將訪問(wèn)權(quán)授予第一個(gè)線程,而第一個(gè)鎖將訪問(wèn)權(quán)授予了第二個(gè)線程。這里,公平鎖在平衡分配方面耗費(fèi)了一定的時(shí)間,這使得第一個(gè)線程獲得第二個(gè)鎖的時(shí)間優(yōu)先于第二個(gè)線程獲得第一個(gè)鎖。這樣,采用不同的鎖,就出現(xiàn)了兩種不同的結(jié)果。
轉(zhuǎn)載注明出處:http://x- spirit.javaeye.com/、http: //www.aygfsteel.com/zhangwei217245/
為了看到公平鎖和非公平鎖性能上的差異,我們不妨將其中線程的睡眠時(shí)間設(shè)定為1毫秒,然后把循環(huán)產(chǎn)生的線程數(shù)提高到5000(修改后的代碼已忽略,自行修改),可以發(fā)現(xiàn),由于公平鎖要維持鎖分配的均衡性,所以,采用公平鎖的程序總運(yùn)行時(shí)間更長(zhǎng)一些。
轉(zhuǎn)載注明出處:http://x- spirit.javaeye.com/、http: //www.aygfsteel.com/zhangwei217245/
根據(jù)運(yùn)行環(huán)境的差異,有些朋友可能并不一定能很直觀的從運(yùn)行結(jié)果中看到兩種不同的鎖帶來(lái)的性能差異。不妨引用IBM開(kāi)發(fā)者社區(qū)的一組測(cè)試結(jié)果來(lái)看一看就行有什么樣的差異吧:
4CPU情況下的同步、非公平鎖和公平鎖吞吐量比較:

單CPU情況下,同步、非公平鎖和公平鎖的吞吐量:


可以看到,同步和公平鎖的吞吐量都是最低的,公平鎖更低一些。但是同步內(nèi)置的監(jiān)控器鎖是不公平的,而且永遠(yuǎn)都是不公平的。而JVM 保證了所有線程最終都會(huì)得到它們所等候的鎖。確保統(tǒng)計(jì)上的公平性,對(duì)多數(shù)情況來(lái)說(shuō),這就已經(jīng)足夠了,而這花費(fèi)的成本則要比絕對(duì)的公平保證的低得多。
轉(zhuǎn)載注明出處:http://x- spirit.javaeye.com/、http: //www.aygfsteel.com/zhangwei217245/
既然Lock這么近乎完美,那我們也許可以忘卻synchronized了。

但是任何事物都是有兩面性的。
1.使用Lock,你必須手動(dòng)的在finally塊中釋放鎖。鎖的獲得和釋放是不受JVM控制的。這要求編程人員更加細(xì)心。
2.當(dāng) JVM 用 synchronized 管理鎖定請(qǐng)求和釋放時(shí),JVM 在生成線程轉(zhuǎn)儲(chǔ)時(shí)能夠包括鎖定信息。這些對(duì)調(diào)試非常有價(jià)值,因?yàn)樗鼈兡軜?biāo)識(shí)死鎖或者其他異常行為的來(lái)源。 Lock 類(lèi)只是普通的類(lèi),JVM 不知道具體哪個(gè)線程擁有 Lock 對(duì)象。
轉(zhuǎn)載注明出處:http://x- spirit.javaeye.com/、http: //www.aygfsteel.com/zhangwei217245/
Lock提供了在多線程爭(zhēng)用的情況下更好的并發(fā)性,但這是以犧牲一定的可維護(hù)性為代價(jià)的。
轉(zhuǎn)載注明出處:http://x- spirit.javaeye.com/、http: //www.aygfsteel.com/zhangwei217245/
所以說(shuō),當(dāng)大量線程發(fā)生爭(zhēng)用的時(shí)候,Lock來(lái)了,大家都讓開(kāi)。