synchronized,ReetrantLock与volatile(二)
為什么80%的碼農(nóng)都做不了架構(gòu)師?>>> ??
volatile?
? ? ?Volatile 變量具有?synchronized?的可見性特性,但是不具備原子特性。這就是說(shuō)線程能夠自動(dòng)發(fā)現(xiàn) volatile 變量的最新值。在理解volatile作用時(shí)候,我們先看看jvm的內(nèi)存模型。
? ??
? ? ?Java內(nèi)存模型規(guī)定,對(duì)于多個(gè)線程共享的變量,存儲(chǔ)在主內(nèi)存當(dāng)中,每個(gè)線程都有自己獨(dú)立的工作內(nèi)存,線程只能訪問(wèn)自己的工作內(nèi)存,不可以訪問(wèn)其它線程的工作內(nèi)存。工作內(nèi)存中保存了主內(nèi)存共享變量的副本,線程要操作這些共享變量,只能通過(guò)操作工作內(nèi)存中的副本來(lái)實(shí)現(xiàn),操作完畢之后再同步回到主內(nèi)存當(dāng)中。
? ? 一個(gè)變量被定義為volatile,那說(shuō)明這個(gè)變量是易變的。如果一個(gè)線程修改了這個(gè)變量,那這個(gè)線程將通知其他線程這個(gè)變量已做修改,工作內(nèi)存保持的變量副本時(shí)效,必須重新從共享內(nèi)存讀取這個(gè)變量的最新值,因此,被volatile非常適合做狀態(tài)標(biāo)識(shí)。
public?class?Volatile?{private?volatile?static?boolean?flag?=?true;public?static?void?work()?{while?(flag)?{System.out.println("=============");}}public?static?void?stop()?{flag?=?false;System.out.println("----stop?work----");}public?static?void?main(String[]?args)?{new?Thread(new?Runnable()?{public?void?run()?{work();}}).start();new?Thread(new?Runnable()?{public?void?run()?{try?{Thread.sleep(100l);}?catch?(InterruptedException?e)?{}stop();}}).start();} }? ? ? 可以看見線程馬上結(jié)束了自己的工作,但是我們?nèi)绻挥胿olatile修飾flag,還是發(fā)現(xiàn)線程也馬上停止了在控制臺(tái)的打印。HotSpot編譯器在server模式和client模式編譯不同, 在client模式下,多線程讀取變量時(shí),都會(huì)直接從主內(nèi)存中去讀取,不會(huì)保存在工作內(nèi)存中,所有加不加volatile效果一樣,而server模式下,則對(duì)代碼做了優(yōu)化。而eclipse中默認(rèn)的啟動(dòng)模式client。
ReetrantLock
? ? ?(畫了一張不知道是什么圖的圖),圖上我們可以看出,ReetrantLock有有屬性Sync sync,FairSync和NonFairSync是Sync的兩個(gè)子類,Sync繼承自AQS(AbstractQueuedSynchronizer)。
? ?
? ??很明顯,ReetrantLock實(shí)現(xiàn)了公平鎖和非公平鎖。通過(guò)源碼我們可以看出ReetrantLock默認(rèn)為非公平鎖,并且提供了一個(gè)boolean參數(shù)的構(gòu)造函數(shù),true為公平鎖,false為非公平鎖。
?public?ReentrantLock(){sync?=?new?NonfairSync();}public?ReentrantLock(boolean?flag){sync?=?((Sync)?(flag???((Sync)?(new?FairSync()))?:?((Sync)?(new?NonfairSync()))));} ? ?現(xiàn)在我們來(lái)看看你ReetrantLock.lock()到底干了什么?
? ? 非公平鎖一開始嘗試檢查state的值,如果是0就通過(guò)CAS看是否能成功置成1,操作如果成功,那么該線程獲得鎖,而公平鎖并沒有,這也就是公平和非公平的區(qū)別。如果非公平鎖對(duì)state的CAS沒有成功,則和公平鎖一樣acquire()。
?//FairSync:protected?final?boolean?tryAcquire(int?i){Thread?thread?=?Thread.currentThread();int?j?=?getState();if(j?==?0){if(!hasQueuedPredecessors()?&&?compareAndSetState(0,?i)){setExclusiveOwnerThread(thread);return?true;}}?elseif(thread?==?getExclusiveOwnerThread()){int?k?=?j?+?i;if(k?<?0){throw?new?Error("Maximum?lock?count?exceeded");}?else{setState(k);return?true;}}return?false;}//NonFairSync:final?boolean?nonfairTryAcquire(int?i){Thread?thread?=?Thread.currentThread();int?j?=?getState();if(j?==?0){if(compareAndSetState(0,?i)){setExclusiveOwnerThread(thread);return?true;}}?elseif(thread?==?getExclusiveOwnerThread()){int?k?=?j?+?i;if(k?<?0){throw?new?Error("Maximum?lock?count?exceeded");}?else{setState(k);return?true;}}return?false;}? ? ?同樣在tryAcquire中,公平鎖在state=0的情況下看了看sync隊(duì)列中有沒有人排在前面(確實(shí)是挺公平的),而非公平鎖完全是不管這條件的,我們?cè)賮?lái)看看acquireQueued(addWaiter(Node.EXCLUSIVE), i),addWaiter是把線程包裝成Node,這里就不貼出來(lái)了。
final?boolean?acquireQueued(final?Node?node,?int?arg)?{boolean?failed?=?true;try?{boolean?interrupted?=?false;for?(;;)?{final?Node?p?=?node.predecessor();//如果當(dāng)前的節(jié)點(diǎn)是head說(shuō)明他是隊(duì)列中第一個(gè)“有效的”節(jié)點(diǎn),因此嘗試獲取if?(p?==?head?&&tryAcquire(arg))?{setHead(node);p.next?=?null;failed?=?false;return?interrupted;}//否則,檢查前一個(gè)節(jié)點(diǎn)的狀態(tài)為,看當(dāng)前獲取鎖失敗的線程是否需要掛起。//如果需要,借助JUC包下的LockSopport類的靜態(tài)方法Park掛起當(dāng)前線程。if?(shouldParkAfterFailedAcquire(p,?node)&&parkAndCheckInterrupt())?interrupted?=?true;}}?finally?{//如果有異常if?(failed)//?取消請(qǐng)求,對(duì)應(yīng)到隊(duì)列操作,就是將當(dāng)前節(jié)點(diǎn)從隊(duì)列中移除。?cancelAcquire(node);} }? ? ? ?到此為止,一個(gè)線程對(duì)于鎖的一次競(jìng)爭(zhēng)才告一段落,結(jié)果又兩種,要么成功獲取到鎖(不用進(jìn)入到AQS隊(duì)列中),要么獲取失敗被掛起,等待下次喚醒后繼續(xù)循環(huán)嘗試獲取鎖,值得注意的是,AQS的隊(duì)列為FIFO隊(duì)列,所以,每次及時(shí)被CPU假喚醒,且當(dāng)前線程不是出在頭節(jié)點(diǎn)的位置,也是會(huì)被掛起的。(為了防止篇幅過(guò)長(zhǎng),AQS中的condition和node將在下篇講)。
? ? ? ?那么問(wèn)題來(lái)了,sychronized和ReentranLock各自要在什么場(chǎng)合用呢?下面貼一下各大博客的總結(jié)吧
? ? ? ?synchronized是托管給JVM執(zhí)行的,而lock是java寫的控制鎖的代碼。在Java1.5中,synchronize是性能低效的。因?yàn)檫@是一個(gè)重量級(jí)操作,需要調(diào)用操作接口,導(dǎo)致有可能加鎖消耗的系統(tǒng)時(shí)間比加鎖以外的操作還多。相比之下使用Java提供的Lock對(duì)象,性能更高一些。但是到了Java1.6,發(fā)生了變化。synchronize在語(yǔ)義上很清晰,可以進(jìn)行很多優(yōu)化,有適應(yīng)自旋,鎖消除,鎖粗化,輕量級(jí)鎖,偏向鎖等等。導(dǎo)致在Java1.6上synchronize的性能并不比Lock差。官方也表示,他們也更支持synchronize,在未來(lái)的版本中還有優(yōu)化余地。
? ? ? ?synchronized原語(yǔ)和ReentrantLock在一般情況下沒有什么區(qū)別,但是在非常復(fù)雜的同步應(yīng)用中,請(qǐng)考慮使用ReentrantLock,特別是遇到下面2種需求的時(shí)候。
? ? ?1.某個(gè)線程在等待一個(gè)鎖的控制權(quán)時(shí)需要中斷
? ? ?2.多個(gè)條件變量或者鎖投票
? ? ?3.時(shí)間鎖等候或者無(wú)塊機(jī)構(gòu)鎖
轉(zhuǎn)載于:https://my.oschina.net/chener/blog/478640
總結(jié)
以上是生活随笔為你收集整理的synchronized,ReetrantLock与volatile(二)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
                            
                        - 上一篇: 怎么改文件类型
 - 下一篇: 饥荒兔毛有什么用? 地下二层兔毛获得方法