java技術(shù)研究

          統(tǒng)計(jì)

          留言簿(3)

          閱讀排行榜

          評(píng)論排行榜

          Lock和synchronized (轉(zhuǎn))

              JDK1.5以后,在鎖機(jī)制方面引入了新的鎖-Lock,在網(wǎng)上的說(shuō)法都比較籠統(tǒng),結(jié)合網(wǎng)上的信息和我的理解這里做個(gè)總結(jié)。 

              java現(xiàn)有的鎖機(jī)制有兩種實(shí)現(xiàn)方式,J.DK1.4前是通過(guò)synchronized實(shí)現(xiàn),JDK1.5后加入java.util.concurrent.locks包下的各種lock(以下簡(jiǎn)稱(chēng)Lock) 

              先說(shuō)說(shuō)代碼層的區(qū)別。 
              synchronized:在代碼里,synchronized類(lèi)似“面向?qū)ο?#8221;,修飾類(lèi)、方法、對(duì)象。 
              Lock:不作為修飾,類(lèi)似“面向過(guò)程”,在方法中需要鎖的時(shí)候lock,在結(jié)束的時(shí)候unlock(一般都在finally塊里)。 
          例如代碼: 

          Java代碼  收藏代碼
          1. public void method1() {  
          2.     synchronized(this){//舊鎖,無(wú)需人工釋放  
          3.         System.out.println(1);  
          4.     }  
          5. }  
          6.       
          7. public void method2() {  
          8.     Lock lock = new ReentrantLock();  
          9.     lock.lock();//上鎖  
          10.     try{  
          11.         System.out.println(2);  
          12.     }finally{  
          13.         lock.unlock();//解鎖  
          14.     }  
          15. }  

              其次說(shuō)說(shuō)性能。 
              相關(guān)的性能測(cè)試網(wǎng)上已經(jīng)有很多,這里也直接拿來(lái)主義,給出結(jié)論: 
              在并發(fā)高是,luck性能優(yōu)勢(shì)很明顯,在低并發(fā)時(shí),synchronized也能取得優(yōu)勢(shì)。具體的臨界范圍比較難定論,下面會(huì)討論。 

              現(xiàn)在來(lái)分析它們具體的區(qū)別。 
              鎖都是 原子性 的,也可以理解為鎖是否在使用的標(biāo)記,并且比較和設(shè)置這個(gè)標(biāo)記的操作是原子性的,不同硬件平臺(tái)上的jdk實(shí)現(xiàn)鎖的相關(guān)方法都是native的(比如park/unpark),所以不同平臺(tái)上鎖的精確度的等級(jí)由這些native的方法決定。所以網(wǎng)上經(jīng)常可以看見(jiàn)的結(jié)論是“Lock比synchronized有更精確的原子操作”說(shuō)的也是native方法(不得不感慨C才是硬件王道)。 


          下面繼續(xù)討論怎么由代碼層到native的過(guò)程。 
          1、所有對(duì)象都自動(dòng)含有單一的鎖,JVM負(fù)責(zé)跟蹤對(duì)象被加鎖的次數(shù)。如果一個(gè)對(duì)象被解鎖,其計(jì)數(shù)變?yōu)?。在任務(wù)(線(xiàn)程)第一次給對(duì)象加鎖的時(shí)候,計(jì)數(shù)變?yōu)?。每當(dāng)這個(gè)相同的任務(wù)(線(xiàn)程)在此對(duì)象上獲得鎖時(shí),計(jì)數(shù)會(huì)遞增。 只有首先獲得鎖的任務(wù)(線(xiàn)程)才能繼續(xù)獲取該對(duì)象上的多個(gè)鎖。每當(dāng)任務(wù)離開(kāi)時(shí),計(jì)數(shù)遞減,當(dāng)計(jì)數(shù)為0的時(shí)候,鎖被完全釋放。synchronized就是基于這個(gè)原理,同時(shí)synchronized靠某個(gè)對(duì)象的單一鎖技術(shù)的次數(shù)來(lái)判斷是否被鎖,所以無(wú)需(也不能)人工干預(yù)鎖的獲取和釋放。如果結(jié)合方法調(diào)用時(shí)的棧和框架(如果對(duì)方法的調(diào)用過(guò)程不熟悉建議看看http://wupuyuan.iteye.com/blog/1157548),不難推測(cè)出synchronized原理是基于棧中的某對(duì)象來(lái)控制一個(gè)框架,所以對(duì)于synchronized有常用的優(yōu)化是鎖對(duì)象不鎖方法。實(shí)際上synchronized作用于方法時(shí),鎖住的是“this”,作用于靜態(tài)方法/屬性時(shí),鎖住的是存在于永久帶的CLASS,相當(dāng)于這個(gè)CLASS的全局鎖,鎖作用于一般對(duì)象時(shí),鎖住的是對(duì)應(yīng)代碼塊。在HotSpot中JVM實(shí)現(xiàn)中,鎖有個(gè)專(zhuān)門(mén)的名字:對(duì)象監(jiān)視器。 


          當(dāng)多個(gè)線(xiàn)程同時(shí)請(qǐng)求某個(gè)對(duì)象監(jiān)視器時(shí),對(duì)象監(jiān)視器會(huì)設(shè)置幾種狀態(tài)用來(lái)區(qū)分請(qǐng)求的線(xiàn)程 
          Contention List:所有請(qǐng)求鎖的線(xiàn)程將被首先放置到該競(jìng)爭(zhēng)隊(duì)列,是個(gè)虛擬隊(duì)列,不是實(shí)際的Queue的數(shù)據(jù)結(jié)構(gòu)。
          Entry List:EntryList與ContentionList邏輯上同屬等待隊(duì)列,ContentionList會(huì)被線(xiàn)程并發(fā)訪(fǎng)問(wèn),為了降低對(duì)ContentionList隊(duì)尾的爭(zhēng)用,而建立EntryList。,Contention List中那些有資格成為候選人的線(xiàn)程被移到Entry List 
          Wait Set:那些調(diào)用wait方法被阻塞的線(xiàn)程被放置到Wait Set 
          OnDeck:任何時(shí)刻最多只能有一個(gè)線(xiàn)程正在競(jìng)爭(zhēng)鎖,該線(xiàn)程稱(chēng)為OnDeck 
          Owner:獲得鎖的線(xiàn)程稱(chēng)為Owner 
          !Owner:釋放鎖的線(xiàn)程 

          2、Lock不同于synchronized面向?qū)ο螅跅V械目蚣芏皇悄硞€(gè)具體對(duì)象,所以L(fǎng)ock只需要在棧里設(shè)置鎖的開(kāi)始和結(jié)束(lock和unlock)的地方就行了(人工必須標(biāo)明),不用關(guān)心框架大小對(duì)象的變化等等。這么做的好處是Lock能提供無(wú)條件的、可輪詢(xún)的、定時(shí)的、可中斷的鎖獲取操作,相對(duì)于synchronized來(lái)說(shuō),synchronized的鎖的獲取是釋放必須在一個(gè)模塊里,獲取和釋放的順序必須相反,而Lock則可以在不同范圍內(nèi)獲取釋放,并且順序無(wú)關(guān)。java.util.concurrent.locks下的鎖類(lèi)很類(lèi)似,依賴(lài)于java.util.concurrent.AbstractQueuedSynchronizer,它們把所有的Lock接口操作都轉(zhuǎn)嫁到Sync類(lèi)上,這個(gè)類(lèi)繼承了AbstractQueuedSynchronizer,它同時(shí)還包含子2個(gè)類(lèi):NonfairSync 和FairSync 從名字上可以看的出是為了實(shí)現(xiàn)公平和非公平性。AbstractQueuedSynchronizer中把所有的的請(qǐng)求線(xiàn)程構(gòu)成一個(gè)隊(duì)列(一樣也是虛擬的),具體的實(shí)現(xiàn)可以參考http://blog.csdn.net/chen77716/article/details/6641477#,這里我就不復(fù)制了。 

          3、從jdk的源代碼來(lái)看,Lock和synchronized的源碼基本相同,區(qū)別主要在維護(hù)的同步隊(duì)列上。再往下深究就到了native方法了。 

          4、還有個(gè)改進(jìn)我也想說(shuō)下,其實(shí)很重要的。線(xiàn)程分阻塞(wait)和非阻塞狀態(tài),阻塞狀態(tài)由操作系統(tǒng)(linux、windows等)完成,當(dāng)前一個(gè)被“鎖”的線(xiàn)程執(zhí)行完畢后,有可能在后續(xù)的線(xiàn)程隊(duì)列里還沒(méi)分配出一個(gè)獲取鎖而被“喚醒”的非阻塞線(xiàn)程,即所有線(xiàn)程還都是阻塞狀態(tài)時(shí),就被系統(tǒng)調(diào)度(進(jìn)入內(nèi)核的線(xiàn)程是阻塞的),這樣會(huì)導(dǎo)致內(nèi)核在用戶(hù)態(tài)和內(nèi)核態(tài)之間來(lái)回接換,嚴(yán)重影響鎖的性能。在jdk1.6以前主要靠自旋鎖來(lái)解決,原理是在前一個(gè)線(xiàn)程結(jié)束后,爭(zhēng)用線(xiàn)程可以做一個(gè)空循環(huán),繼續(xù)占有CPU,等待取鎖的機(jī)會(huì)。當(dāng)然這樣做顯然也是浪費(fèi)時(shí)間,只是在兩種浪費(fèi)中選取浪費(fèi)少的……  jdk1.6后引入了偏向鎖,當(dāng)線(xiàn)程第一次獲得了監(jiān)視對(duì)象,之后讓監(jiān)視對(duì)象“偏向”這個(gè)線(xiàn)程,之后的多次調(diào)用則可以避免CAS操作,等于是置了一臨時(shí)變量來(lái)記錄位置(類(lèi)似索引比較)。詳細(xì)的就涉及到匯編指令了,我也就沒(méi)太深究,偏向鎖性能優(yōu)于自旋鎖,但是還是沒(méi)有達(dá)到HotSpot認(rèn)為的最佳時(shí)間(一個(gè)線(xiàn)程上下文切換的時(shí)間)。 

              綜合來(lái)看對(duì)于所有的高并發(fā)情況,采用Lock加鎖是最優(yōu)選擇,但是由于歷史遺留等問(wèn)題,synchronized也還是不能完全被淘汰,同時(shí),在低并發(fā)情況下,synchronized的性能還是比Lock好的。 

          原帖地址:http://wupuyuan.iteye.com/blog/1158655

          posted on 2015-10-27 19:08 小秦 閱讀(314) 評(píng)論(0)  編輯  收藏


          只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 邵东县| 怀宁县| 福泉市| 洮南市| 内丘县| 玛曲县| 新化县| 伊金霍洛旗| 长春市| 绥江县| 石河子市| 通州市| 临湘市| 琼海市| 会宁县| 阜南县| 三门峡市| 苗栗县| 门源| 额济纳旗| 常熟市| 柳河县| 克东县| 阿巴嘎旗| 临汾市| 图们市| 台东县| 江孜县| 丹棱县| 卢氏县| 云林县| 英山县| 南投市| 成武县| 西藏| 博野县| 常山县| 习水县| 临潭县| 敖汉旗| 泗阳县|