jinfeng_wang

          G-G-S,D-D-U!

          BlogJava 首頁(yè) 新隨筆 聯(lián)系 聚合 管理
            400 Posts :: 0 Stories :: 296 Comments :: 0 Trackbacks
          http://www.mamicode.com/info-detail-334916.html

          一、自旋鎖提出的背景

                由于在多處理器系統(tǒng)環(huán)境中有些資源因?yàn)槠溆邢扌裕袝r(shí)需要互斥訪問(wèn)(mutual exclusion),這時(shí)會(huì)引入鎖的機(jī)制,只有獲取了鎖的進(jìn)程才能獲取資源訪問(wèn)。即是每次只能有且只有一個(gè)進(jìn)程能獲取鎖,才能進(jìn)入自己的臨界區(qū),同一時(shí)間不能兩個(gè)或兩個(gè)以上進(jìn)程進(jìn)入臨界區(qū),當(dāng)退出臨界區(qū)時(shí)釋放鎖。設(shè)計(jì)互斥算法時(shí)總是會(huì)面臨一種情況,即沒(méi)有獲得鎖的進(jìn)程怎么辦?通常有2種處理方式。一種是沒(méi)有獲得鎖的調(diào)用者就一直循環(huán)在那里看是否該自旋鎖的保持者已經(jīng)釋放了鎖,這就是自旋鎖,他不用將縣城阻塞起來(lái)(NON-BLOCKING);另一種是沒(méi)有獲得鎖的進(jìn)程就阻塞(BLOCKING)自己,請(qǐng)求OS調(diào)度另一個(gè)線程上處理器,這就是互斥鎖。

           

          二、自旋鎖原理

                跟互斥鎖一樣,一個(gè)執(zhí)行單元要想訪問(wèn)被自旋鎖保護(hù)的共享資源,必須先得到鎖,在訪問(wèn)完共享資源后,必須釋放鎖。如果在獲取自旋鎖時(shí),沒(méi)有任何執(zhí)行單元保持該鎖,那么將立即得到鎖;如果在獲取自旋鎖時(shí)鎖已經(jīng)有保持者,那么獲取鎖操作將自旋在那里,直到該自旋鎖的保持者釋放了鎖。由此我們可以看出,自旋鎖是一種比較低級(jí)的保護(hù)數(shù)據(jù)結(jié)構(gòu)或代碼片段的原始方式,這種鎖可能存在兩個(gè)問(wèn)題:

          • 遞歸死鎖:試圖遞歸地獲得自旋鎖必然會(huì)引起死鎖:遞歸程序的持有實(shí)例在第二個(gè)實(shí)例循環(huán),以試圖獲得相同自旋鎖時(shí),不會(huì)釋放此自旋鎖。在遞歸程序中使用自旋鎖應(yīng)遵守下列策略:遞歸程序決不能在持有自旋鎖時(shí)調(diào)用它自己,也決不能在遞歸調(diào)用時(shí)試圖獲得相同的自旋鎖。此外如果一個(gè)進(jìn)程已經(jīng)將資源鎖定,那么,即使其它申請(qǐng)這個(gè)資源的進(jìn)程不停地瘋狂“自旋”,也無(wú)法獲得資源,從而進(jìn)入死循環(huán)。
          • 過(guò)多占用cpu資源。如果不加限制,由于申請(qǐng)者一直在循環(huán)等待,因此自旋鎖在鎖定的時(shí)候,如果不成功,不會(huì)睡眠,會(huì)持續(xù)的嘗試,單cpu的時(shí)候自旋鎖會(huì)讓其它process動(dòng)不了. 因此,一般自旋鎖實(shí)現(xiàn)會(huì)有一個(gè)參數(shù)限定最多持續(xù)嘗試次數(shù). 超出后, 自旋鎖放棄當(dāng)前time slice. 等下一次機(jī)會(huì)

          由此可見(jiàn),自旋鎖比較適用于鎖使用者保持鎖時(shí)間比較短的情況。正是由于自旋鎖使用者一般保持鎖時(shí)間非常短,因此選擇自旋而不是睡眠是非常必要的,自旋鎖的效率遠(yuǎn)高于互斥鎖。

           

          三、Java CAS

                CAS是一種系統(tǒng)原語(yǔ)(所謂原語(yǔ)屬于操作系統(tǒng)用語(yǔ)范疇。原語(yǔ)由若干條指令組成的,用于完成一定功能的一個(gè)過(guò)程。primitive or atomic action 是由若干個(gè)機(jī)器指令構(gòu)成的完成某種特定功能的一段程序,具有不可分割性·即原語(yǔ)的執(zhí)行必須是連續(xù)的,在執(zhí)行過(guò)程中不允許被中斷)。CAS是Compare And Set的縮寫。CAS有3個(gè)操作數(shù),內(nèi)存值V,舊的預(yù)期值A(chǔ),要修改的新值B。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時(shí),將內(nèi)存值V修改為B,否則什么都不做。

               在x86 平臺(tái)上,CPU提供了在指令執(zhí)行期間對(duì)總線加鎖的手段。CPU芯片上有一條引線#HLOCK pin,如果匯編語(yǔ)言的程序中在一條指令前面加上前綴"LOCK",經(jīng)過(guò)匯編以后的機(jī)器代碼就使CPU在執(zhí)行這條指令的時(shí)候把#HLOCK pin的電位拉低,持續(xù)到這條指令結(jié)束時(shí)放開(kāi),從而把總線鎖住,這樣同一總線上別的CPU就暫時(shí)不能通過(guò)總線訪問(wèn)內(nèi)存了,保證了這條指令在多處理器環(huán)境中的原子性

                sun.misc.Unsafe是JDK里面的一個(gè)內(nèi)部類,這個(gè)類為JDK嚴(yán)格保護(hù),因?yàn)樗峁┝舜罅康牡图?jí)的內(nèi)存操作和系統(tǒng)功能。如果因?yàn)殄e(cuò)誤的使用了這個(gè)類,不會(huì)有“異常”被扔出,甚至?xí)斐蒍VM宕機(jī)。這也是為什么這個(gè)類的名字是Unsafe的原因。因此當(dāng)使用這個(gè)類的時(shí)候,你一定要明白你在干什么。這個(gè)類中提供了3個(gè)CAS的操作

          方法名解釋
          compareAndSwapInt(Object object, long address, int expected, int newValue) 比較對(duì)象object的某個(gè)int型的屬性(以地址的方式訪問(wèn)),如果他的數(shù)據(jù)值是expected,則設(shè)定為newValue,返回true;否則返回false
          compareAndSwapLong(Object object, long address, long expected, long newValue) 比較對(duì)象object的某個(gè)long型的屬性(以地址的方式訪問(wèn)),如果他的數(shù)據(jù)值是expected,則設(shè)定為newValue,返回true;否則返回false
          compareAndSwapLong(Object object, long address, Object expected, Object newValue) 比較對(duì)象object的某個(gè)Object型的屬性(以地址的方式訪問(wèn)),如果他的數(shù)據(jù)值是expected,則設(shè)定為newValue,返回true;否則返回false

           

          四、Java自旋鎖應(yīng)用-原子包

          Jdk1.5以后,提供了java.util.concurrent.atomic包,這個(gè)包里面提供了一組原子類。其基本的特性就是在多線程環(huán)境下,當(dāng)有多個(gè)線程同時(shí)執(zhí)行這些類的實(shí)例包含的方法時(shí),具有排他性,即當(dāng)某個(gè)線程進(jìn)入方法,執(zhí)行其中的指令時(shí),不會(huì)被其他線程打斷,而別的線程就像自旋鎖一樣,一直等到該方法執(zhí)行完成,才由JVM從等待隊(duì)列中選擇一個(gè)另一個(gè)線程進(jìn)入,這只是一種邏輯上的理解。實(shí)際上是借助硬件的相關(guān)指令來(lái)實(shí)現(xiàn)的,不會(huì)阻塞線程(或者說(shuō)只是在硬件級(jí)別上阻塞了)。其中的類可以分成4組

          • AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
          • AtomicIntegerArray,AtomicLongArray
          • AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater
          • AtomicMarkableReference,AtomicStampedReference,AtomicReferenceArray

           

          我們來(lái)看一段AtomicBoolean中的自旋鎖的代碼

          public final boolean getAndSet(boolean newValue) {    for (;;) {        boolean current = get();        if (compareAndSet(current, newValue))            return current;    } }

           

           

          參考

          http://baike.baidu.com/view/1250961.htm?fr=aladdin

          posted on 2016-12-14 13:56 jinfeng_wang 閱讀(599) 評(píng)論(0)  編輯  收藏

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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 黑河市| 涡阳县| 丹凤县| 巩留县| 资溪县| 肥东县| 舞阳县| 通许县| 陆丰市| 湘潭县| 铜鼓县| 遂平县| 安丘市| 香港 | 会泽县| 民权县| 克什克腾旗| 九龙城区| 梁山县| 乐山市| 宁都县| 津市市| 太白县| 夏河县| 阿巴嘎旗| 济南市| 濮阳县| 博乐市| 军事| 黄龙县| 民和| 花莲市| 温泉县| 乌鲁木齐市| 中阳县| 富平县| 罗江县| 仙游县| 海伦市| 杂多县| 邛崃市|