java中經(jīng)典的JVM鎖有哪些

本篇內(nèi)容介紹了“java中經(jīng)典的JVM鎖有哪些”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

成都創(chuàng)新互聯(lián)是一家專業(yè)的成都網(wǎng)站建設(shè)公司,我們專注網(wǎng)站建設(shè)、網(wǎng)站制作、網(wǎng)絡(luò)營(yíng)銷、企業(yè)網(wǎng)站建設(shè),賣鏈接,廣告投放為企業(yè)客戶提供一站式建站解決方案,能帶給客戶新的互聯(lián)網(wǎng)理念。從網(wǎng)站結(jié)構(gòu)的規(guī)劃UI設(shè)計(jì)到用戶體驗(yàn)提高,創(chuàng)新互聯(lián)力求做到盡善盡美。

synchronized synchronized關(guān)鍵字是一把經(jīng)典的鎖,也是我們平時(shí)用得最多的。在jdk1.6之前,syncronized是一把重量級(jí)的鎖,不過隨著jdk的升級(jí),也在對(duì)它進(jìn)行不斷的優(yōu)化,如今它變得不那么重了,甚至在某些場(chǎng)景下,它的性能反而優(yōu)于輕量級(jí)鎖。在加了syncronized關(guān)鍵字的方法、代碼塊中,一次只允許一個(gè)線程進(jìn)入特定代碼段,從而避免多線程同時(shí)修改同一數(shù)據(jù)。synchronized鎖有如下幾個(gè)特點(diǎn):

a、有鎖升級(jí)過程 在jdk1.5(含)之前,synchronized的底層實(shí)現(xiàn)是重量級(jí)的,所以之前一直稱呼它為"重量級(jí)鎖",在jdk1.5之后,對(duì)synchronized進(jìn)行了各種優(yōu)化,它變得不那么重了,實(shí)現(xiàn)原理就是鎖升級(jí)的過程。我們先聊聊1.5之后的synchronized實(shí)現(xiàn)原理是怎樣的。說到synchronized加鎖原理,就不得不先說java對(duì)象在內(nèi)存中的布局,java對(duì)象內(nèi)存布局如下:

java中經(jīng)典的JVM鎖有哪些

如上圖所示,在創(chuàng)建一個(gè)對(duì)象后,在JVM虛擬機(jī)(HotSpot)中,對(duì)象在Java內(nèi)存中的存儲(chǔ)布局 可分為三塊:**(1)對(duì)象頭區(qū)域

**此處存儲(chǔ)的信息包括兩部分:

  • 對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù)(MarkWord)

存儲(chǔ)hashCode、GC分代年齡、鎖類型標(biāo)記、偏向鎖線程ID、CAS鎖指向線程LockRecord的指針等,synconized鎖的機(jī)制與這個(gè)部分(markwork)密切相關(guān),用markword中最低的三位代表鎖的狀態(tài),其中一位是偏向鎖位,另外兩位是普通鎖位

  • 對(duì)象類型指針(Class Pointer)

對(duì)象指向它的類元數(shù)據(jù)的指針、JVM就是通過它來確定是哪個(gè)Class的實(shí)例(2)實(shí)例數(shù)據(jù)區(qū)域 此處存儲(chǔ)的是對(duì)象真正有效的信息,比如對(duì)象中所有字段的內(nèi)容

(3)對(duì)齊填充區(qū)域 JVM的實(shí)現(xiàn)HostSpot規(guī)定對(duì)象的起始地址必須是8字節(jié)的整數(shù)倍,換句話來說,現(xiàn)在64位的OS往外讀取數(shù)據(jù)的時(shí)候一次性讀取64bit整數(shù)倍的數(shù)據(jù),也就是8個(gè)字節(jié),所以HotSpot為了高效讀取對(duì)象,就做了"對(duì)齊",如果一個(gè)對(duì)象實(shí)際占的內(nèi)存大小不是8byte的整數(shù)倍時(shí),就"補(bǔ)位"到8byte的整數(shù)倍。所以對(duì)齊填充區(qū)域的大小不是固定的。

當(dāng)線程進(jìn)入到synchronized處嘗試獲取該鎖時(shí),synchronized鎖升級(jí)流程如下:

java中經(jīng)典的JVM鎖有哪些

如上圖所示,synchronized鎖升級(jí)的順序?yàn)椋?偏向鎖->輕量級(jí)鎖->重量級(jí)鎖,每一步觸發(fā)鎖升級(jí)的情況如下:偏向鎖在JDK1.8中,其實(shí)默認(rèn)是輕量級(jí)鎖,但如果設(shè)定了-XX:BiasedLockingStartupDelay = 0,那在對(duì)一個(gè)Object做syncronized的時(shí)候,會(huì)立即上一把偏向鎖。當(dāng)處于偏向鎖狀態(tài)時(shí),markwork會(huì)記錄當(dāng)前線程ID升級(jí)到輕量級(jí)鎖當(dāng)下一個(gè)線程參與到偏向鎖競(jìng)爭(zhēng)時(shí),會(huì)先判斷markword中保存的線程ID是否與這個(gè)線程ID相等,如果不相等,會(huì)立即撤銷偏向鎖,升級(jí)為輕量級(jí)鎖。每個(gè)線程在自己的線程棧中生成一個(gè)LockRecord(LR),然后每個(gè)線程通過CAS(自旋)的操作將鎖對(duì)象頭中的markwork設(shè)置為指向自己的LR的指針,哪個(gè)線程設(shè)置成功,就意味著獲得鎖。關(guān)于synchronized中此時(shí)執(zhí)行的CAS操作是通過native的調(diào)用HotSpot中bytecodeInterpreter.cpp文件C++代碼實(shí)現(xiàn)的,有興趣的可以繼續(xù)深挖升級(jí)到重量級(jí)鎖如果鎖競(jìng)爭(zhēng)加劇(如線程自旋次數(shù)或者自旋的線程數(shù)超過某閾值,JDK1.6之后,由JVM自己控制改規(guī)則),就會(huì)升級(jí)為重量級(jí)鎖。此時(shí)就會(huì)向操作系統(tǒng)申請(qǐng)資源,線程掛起,進(jìn)入到操作系統(tǒng)內(nèi)核態(tài)的等待隊(duì)列中,等待操作系統(tǒng)調(diào)度,然后映射回用戶態(tài)。在重量級(jí)鎖中,由于需要做內(nèi)核態(tài)到用戶態(tài)的轉(zhuǎn)換,而這個(gè)過程中需要消耗較多時(shí)間,也就是"重"的原因之一。

b、可重入synchronized擁有強(qiáng)制原子性的內(nèi)部鎖機(jī)制,是一把可重入鎖。因此,在一個(gè)線程使用synchronized方法時(shí)調(diào)用該對(duì)象另一個(gè)synchronized方法,即一個(gè)線程得到一個(gè)對(duì)象鎖后再次請(qǐng)求該對(duì)象鎖,是永遠(yuǎn)可以拿到鎖的。在Java中線程獲得對(duì)象鎖的操作是以線程為單位的,而不是以調(diào)用為單位的。synchronized鎖的對(duì)象頭的markwork中會(huì)記錄該鎖的線程持有者和計(jì)數(shù)器,當(dāng)一個(gè)線程請(qǐng)求成功后,JVM會(huì)記下持有鎖的線程,并將計(jì)數(shù)器計(jì)為1。此時(shí)其他線程請(qǐng)求該鎖,則必須等待。而該持有鎖的線程如果再次請(qǐng)求這個(gè)鎖,就可以再次拿到這個(gè)鎖,同時(shí)計(jì)數(shù)器會(huì)遞增。當(dāng)線程退出一個(gè)synchronized方法/塊時(shí),計(jì)數(shù)器會(huì)遞減,如果計(jì)數(shù)器為0則釋放該鎖鎖。

c、悲觀鎖(互斥鎖、排他鎖)synchronized是一把悲觀鎖(獨(dú)占鎖),當(dāng)前線程如果獲取到鎖,會(huì)導(dǎo)致其它所有需要鎖該的線程等待,一直等待持有鎖的線程釋放鎖才繼續(xù)進(jìn)行鎖的爭(zhēng)搶

ReentrantLock**ReentrantLock從字面可以看出是一把可重入鎖,這點(diǎn)和synchronized一樣,但實(shí)現(xiàn)原理也與syncronized有很大差別,它是基于經(jīng)典的AQS(AbstractQueueSyncronized)實(shí)現(xiàn)的,AQS是基于volitale和CAS實(shí)現(xiàn)的,其中AQS中維護(hù)一個(gè)valitale類型的變量state來做一個(gè)可重入鎖的重入次數(shù),加鎖和釋放鎖也是圍繞這個(gè)變量來進(jìn)行的。ReentrantLock也提供了一些synchronized沒有的特點(diǎn),因此比synchronized好用AQS模型如下圖:

java中經(jīng)典的JVM鎖有哪些

ReentrantLock有如下特點(diǎn):a、可重入 ReentrantLock和syncronized關(guān)鍵字一樣,都是可重入鎖,不過兩者實(shí)現(xiàn)原理稍有差別,RetrantLock利用AQS的的state狀態(tài)來判斷資源是否已鎖,同一線程重入加鎖,state的狀態(tài)+1; 同一線程重入解鎖,state狀態(tài)-1(解鎖必須為當(dāng)前獨(dú)占線程,否則異常); 當(dāng)state為0時(shí)解鎖成功。b、需要手動(dòng)加鎖、解鎖 synchronized關(guān)鍵字是自動(dòng)進(jìn)行加鎖、解鎖的,而ReentrantLock需要lock()和unlock()方法配合try/finally語(yǔ)句塊來完成,來手動(dòng)加鎖、解鎖c、支持設(shè)置鎖的超時(shí)時(shí)間 synchronized關(guān)鍵字無法設(shè)置鎖的超時(shí)時(shí)間,如果一個(gè)獲得鎖的線程內(nèi)部發(fā)生死鎖,那么其他線程就會(huì)一直進(jìn)入阻塞狀態(tài),而ReentrantLock提供tryLock方法,允許設(shè)置線程獲取鎖的超時(shí)時(shí)間,如果超時(shí),則跳過,不進(jìn)行任何操作,避免死鎖的發(fā)生d、支持公平/非公平鎖 synchronized關(guān)鍵字是一種非公平鎖,先搶到鎖的線程先執(zhí)行。而ReentrantLock的構(gòu)造方法中允許設(shè)置true/false來實(shí)現(xiàn)公平、非公平鎖,如果設(shè)置為true,則線程獲取鎖要遵循"先來后到"的規(guī)則,每次都會(huì)構(gòu)造一個(gè)線程N(yùn)ode,然后到雙向鏈表的"尾巴"后面排隊(duì),等待前面的Node釋放鎖資源。e、可中斷鎖ReentrantLock中的lockInterruptibly()方法使得線程可以在被阻塞時(shí)響應(yīng)中斷,比如一個(gè)線程t1通過lockInterruptibly()方法獲取到一個(gè)可重入鎖,并執(zhí)行一個(gè)長(zhǎng)時(shí)間的任務(wù),另一個(gè)線程通過interrupt()方法就可以立刻打斷t1線程的執(zhí)行,來獲取t1持有的那個(gè)可重入鎖。而通過ReentrantLock的lock()方法或者Synchronized持有鎖的線程是不會(huì)響應(yīng)其他線程的interrupt()方法的,直到該方法主動(dòng)釋放鎖之后才會(huì)響應(yīng)interrupt()方法。

ReentrantReadWriteLockReentrantReadWriteLock(讀寫鎖)其實(shí)是兩把鎖,一把是WriteLock(寫鎖),一把是讀鎖,ReadLock。讀寫鎖的規(guī)則是:讀讀不互斥、讀寫互斥、寫寫互斥。在一些實(shí)際的場(chǎng)景中,讀操作的頻率遠(yuǎn)遠(yuǎn)高于寫操作,如果直接用一般的鎖進(jìn)行并發(fā)控制的話,就會(huì)讀讀互斥、讀寫互斥、寫寫互斥,效率低下,讀寫鎖的產(chǎn)生就是為了優(yōu)化這種場(chǎng)景的操作效率。一般情況下獨(dú)占鎖的效率低來源于高并發(fā)下對(duì)臨界區(qū)的激烈競(jìng)爭(zhēng)導(dǎo)致線程上下文切換。因此當(dāng)并發(fā)不是很高的情況下,讀寫鎖由于需要額外維護(hù)讀鎖的狀態(tài),可能還不如獨(dú)占鎖的效率高。因此需要根據(jù)實(shí)際情況選擇使用。ReentrantReadWriteLock的原理也是基于AQS進(jìn)行實(shí)現(xiàn)的,與ReentrantLock的差別在于ReentrantReadWriteLock鎖擁有共享鎖、排他鎖屬性。讀寫鎖中的加鎖、釋放鎖也是基于Sync(繼承于AQS),并且主要使用AQS中的state和node中的waitState變量進(jìn)行實(shí)現(xiàn)的。實(shí)現(xiàn)讀寫鎖與實(shí)現(xiàn)普通互斥鎖的主要區(qū)別在于需要分別記錄讀鎖狀態(tài)及寫鎖狀態(tài),并且等待隊(duì)列中需要區(qū)別處理兩種加鎖操作。ReentrantReadWriteLock中將AQS中的int類型的state分為高16位與第16位分別記錄讀鎖和寫鎖的狀態(tài),如下圖所示:

java中經(jīng)典的JVM鎖有哪些

a、WriteLock(寫鎖)是悲觀鎖(排他鎖、互斥鎖)通過計(jì)算 state&((1<<16)-1),將state的高16位全部抹去,因此state的低位記錄著寫鎖的重入計(jì)數(shù)

獲取寫鎖源碼:

/**         * 獲取寫鎖           Acquires the write lock.         *  如果此時(shí)沒有任何線程持有寫鎖或者讀鎖,那么當(dāng)前線程執(zhí)行CAS操作更新status,         *  若更新成功,則設(shè)置讀鎖重入次數(shù)為1,并立即返回         * <p>Acquires the write lock if neither the read nor write lock         * are held by another thread         * and returns immediately, setting the write lock hold count to         * one.         *  如果當(dāng)前線程已經(jīng)持有該寫鎖,那么將寫鎖持有次數(shù)設(shè)置為1,并立即返回         * <p>If the current thread already holds the write lock then the         * hold count is incremented by one and the method returns         * immediately.         *  如果該鎖已經(jīng)被另外一個(gè)線程持有,那么停止該線程的CPU調(diào)度并進(jìn)入休眠狀態(tài),         *  直到該寫鎖被釋放,且成功將寫鎖持有次數(shù)設(shè)置為1才表示獲取寫鎖成功         * <p>If the lock is held by another thread then the current         * thread becomes disabled for thread scheduling purposes and         * lies dormant until the write lock has been acquired, at which         * time the write lock hold count is set to one.         */        public void lock() {            sync.acquire(1);        }/**     * 該方法為以獨(dú)占模式獲取鎖,忽略中斷     * 如果調(diào)用一次該“tryAcquire”方法更新status成功,則直接返回,代表?yè)屾i成功     * 否則,將會(huì)進(jìn)入同步隊(duì)列等待,不斷執(zhí)行“tryAcquire”方法嘗試CAS更新status狀態(tài),直到成功搶到鎖     * 其中“tryAcquire”方法在NonfairSync(公平鎖)中和FairSync(非公平鎖)中都有各自的實(shí)現(xiàn)     *     * Acquires in exclusive mode, ignoring interrupts.  Implemented     * by invoking at least once {@link #tryAcquire},     * returning on success.  Otherwise the thread is queued, possibly     * repeatedly blocking and unblocking, invoking {@link     * #tryAcquire} until success.  This method can be used     * to implement method {@link Lock#lock}.     *     * @param arg the acquire argument.  This value is conveyed to     *        {@link #tryAcquire} but is otherwise uninterpreted and     *        can represent anything you like.     */    public final void acquire(int arg) {        if (!tryAcquire(arg) &&            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))            selfInterrupt();    }    protected final boolean tryAcquire(int acquires) {            /*             * Walkthrough:             * 1、如果讀寫鎖的計(jì)數(shù)不為0,且持有鎖的線程不是當(dāng)前線程,則返回false             * 1. If read count nonzero or write count nonzero             *    and owner is a different thread, fail.             * 2、如果持有鎖的計(jì)數(shù)不為0且計(jì)數(shù)總數(shù)超過限定的最大值,也返回false             * 2. If count would saturate, fail. (This can only             *    happen if count is already nonzero.)             * 3、如果該鎖是可重入或該線程在隊(duì)列中的策略是允許它嘗試搶鎖,那么該線程就能獲取鎖             * 3. Otherwise, this thread is eligible for lock if             *    it is either a reentrant acquire or             *    queue policy allows it. If so, update state             *    and set owner.             */            Thread current = Thread.currentThread();            //獲取讀寫鎖的狀態(tài)            int c = getState();            //獲取該寫鎖重入的次數(shù)            int w = exclusiveCount(c);            //如果讀寫鎖狀態(tài)不為0,說明已經(jīng)有其他線程獲取了讀鎖或?qū)戞i            if (c != 0) {                //如果寫鎖重入次數(shù)為0,說明有線程獲取到讀鎖,根據(jù)“讀寫鎖互斥”原則,返回false                //或者如果寫鎖重入次數(shù)不為0,且獲取寫鎖的線程不是當(dāng)前線程,根據(jù)"寫鎖獨(dú)占"原則,返回false                // (Note: if c != 0 and w == 0 then shared count != 0)                if (w == 0 || current != getExclusiveOwnerThread())                    return false;               //如果寫鎖可重入次數(shù)超過最大次數(shù)(65535),則拋異常                if (w + exclusiveCount(acquires) > MAX_COUNT)                    throw new Error("Maximum lock count exceeded");                //到這里說明該線程是重入寫鎖,更新重入寫鎖的計(jì)數(shù)(+1),返回true                // Reentrant acquire                setState(c + acquires);                return true;            }            //如果讀寫鎖狀態(tài)為0,說明讀鎖和寫鎖都沒有被獲取,會(huì)走下面兩個(gè)分支:            //如果要阻塞或者執(zhí)行CAS操作更新讀寫鎖的狀態(tài)失敗,則返回false            //如果不需要阻塞且CAS操作成功,則當(dāng)前線程成功拿到鎖,設(shè)置鎖的owner為當(dāng)前線程,返回true            if (writerShouldBlock() ||                !compareAndSetState(c, c + acquires))                return false;            setExclusiveOwnerThread(current);            return true;        }

釋放寫鎖源碼:

/*  * Note that tryRelease and tryAcquire can be called by  * Conditions. So it is possible that their arguments contain  * both read and write holds that are all released during a  * condition wait and re-established in tryAcquire.  */ protected final boolean tryRelease(int releases) {     //若鎖的持有者不是當(dāng)前線程,拋出異常     if (!isHeldExclusively())         throw new IllegalMonitorStateException();     //寫鎖的可重入計(jì)數(shù)減掉releases個(gè)     int nextc = getState() - releases;     //如果寫鎖重入計(jì)數(shù)為0了,則說明寫鎖被釋放了     boolean free = exclusiveCount(nextc) == 0;     if (free)        //若寫鎖被釋放,則將鎖的持有者設(shè)置為null,進(jìn)行GC        setExclusiveOwnerThread(null);     //更新寫鎖的重入計(jì)數(shù)     setState(nextc);     return free; }

b、ReadLock(讀鎖)是共享鎖(樂觀鎖)通過計(jì)算 state>>>16 進(jìn)行無符號(hào)補(bǔ)0,右移16位,因此state的高位記錄著寫鎖的重入計(jì)數(shù) 讀鎖獲取鎖的過程比寫鎖稍微復(fù)雜些,首先判斷寫鎖是否為0并且當(dāng)前線程不占有獨(dú)占鎖,直接返回;否則,判斷讀線程是否需要被阻塞并且讀鎖數(shù)量是否小于最大值并且比較設(shè)置狀態(tài)成功,若當(dāng)前沒有讀鎖,則設(shè)置第一個(gè)讀線程firstReader和firstReaderHoldCount;若當(dāng)前線程線程為第一個(gè)讀線程,則增加firstReaderHoldCount;否則,將設(shè)置當(dāng)前線程對(duì)應(yīng)的HoldCounter對(duì)象的值,更新成功后會(huì)在firstReaderHoldCount中readHolds(ThreadLocal類型的)的本線程副本中記錄當(dāng)前線程重入數(shù),這是為了實(shí)現(xiàn)jdk1.6中加入的getReadHoldCount()方法的,這個(gè)方法能獲取當(dāng)前線程重入共享鎖的次數(shù)(state中記錄的是多個(gè)線程的總重入次數(shù)),加入了這個(gè)方法讓代碼復(fù)雜了不少,但是其原理還是很簡(jiǎn)單的:如果當(dāng)前只有一個(gè)線程的話,還不需要?jiǎng)佑肨hreadLocal,直接往firstReaderHoldCount這個(gè)成員變量里存重入數(shù),當(dāng)有第二個(gè)線程來的時(shí)候,就要?jiǎng)佑肨hreadLocal變量readHolds了,每個(gè)線程擁有自己的副本,用來保存自己的重入數(shù)。

獲取讀鎖源碼:

/**         * 獲取讀鎖         * Acquires the read lock.         * 如果寫鎖未被其他線程持有,執(zhí)行CAS操作更新status值,獲取讀鎖后立即返回         * <p>Acquires the read lock if the write lock is not held by         * another thread and returns immediately.         *          * 如果寫鎖被其他線程持有,那么停止該線程的CPU調(diào)度并進(jìn)入休眠狀態(tài),直到該讀鎖被釋放         * <p>If the write lock is held by another thread then         * the current thread becomes disabled for thread scheduling         * purposes and lies dormant until the read lock has been acquired.         */        public void lock() {            sync.acquireShared(1);        }   /**     * 該方法為以共享模式獲取讀鎖,忽略中斷     * 如果調(diào)用一次該“tryAcquireShared”方法更新status成功,則直接返回,代表?yè)屾i成功     * 否則,將會(huì)進(jìn)入同步隊(duì)列等待,不斷執(zhí)行“tryAcquireShared”方法嘗試CAS更新status狀態(tài),直到成功搶到鎖     * 其中“tryAcquireShared”方法在NonfairSync(公平鎖)中和FairSync(非公平鎖)中都有各自的實(shí)現(xiàn)     * (看這注釋是不是和寫鎖很對(duì)稱)     * Acquires in shared mode, ignoring interrupts.  Implemented by     * first invoking at least once {@link #tryAcquireShared},     * returning on success.  Otherwise the thread is queued, possibly     * repeatedly blocking and unblocking, invoking {@link     * #tryAcquireShared} until success.     *     * @param arg the acquire argument.  This value is conveyed to     *        {@link #tryAcquireShared} but is otherwise uninterpreted     *        and can represent anything you like.     */    public final void acquireShared(int arg) {        if (tryAcquireShared(arg) < 0)            doAcquireShared(arg);    }    protected final int tryAcquireShared(int unused) {            /*             * Walkthrough:             * 1、如果已經(jīng)有其他線程獲取到了寫鎖,根據(jù)“讀寫互斥”原則,搶鎖失敗,返回-1             * 1.If write lock held by another thread, fail.             * 2、如果該線程本身持有寫鎖,那么看一下是否要readerShouldBlock,如果不需要阻塞,             *    則執(zhí)行CAS操作更新state和重入計(jì)數(shù)。             *    這里要注意的是,上面的步驟不檢查是否可重入(因?yàn)樽x鎖屬于共享鎖,天生支持可重入)             * 2. Otherwise, this thread is eligible for             *    lock wrt state, so ask if it should block             *    because of queue policy. If not, try             *    to grant by CASing state and updating count.             *    Note that step does not check for reentrant             *    acquires, which is postponed to full version             *    to avoid having to check hold count in             *    the more typical non-reentrant case.             * 3、如果因?yàn)镃AS更新status失敗或者重入計(jì)數(shù)超過最大值導(dǎo)致步驟2執(zhí)行失敗             *    那就進(jìn)入到fullTryAcquireShared方法進(jìn)行死循環(huán),直到搶鎖成功             * 3. If step 2 fails either because thread             *    apparently not eligible or CAS fails or count             *    saturated, chain to version with full retry loop.             */                    //當(dāng)前嘗試獲取讀鎖的線程            Thread current = Thread.currentThread();            //獲取該讀寫鎖狀態(tài)            int c = getState();            //如果有線程獲取到了寫鎖 ,且獲取寫鎖的不是當(dāng)前線程則返回失敗            if (exclusiveCount(c) != 0 &&                getExclusiveOwnerThread() != current)                return -1;            //獲取讀鎖的重入計(jì)數(shù)            int r = sharedCount(c);            //如果讀線程不應(yīng)該被阻塞,且重入計(jì)數(shù)小于最大值,且CAS執(zhí)行讀鎖重入計(jì)數(shù)+1成功,則執(zhí)行線程重入的計(jì)數(shù)加1操作,返回成功            if (!readerShouldBlock() &&                r < MAX_COUNT &&                compareAndSetState(c, c + SHARED_UNIT)) {                //如果還未有線程獲取到讀鎖,則將firstReader設(shè)置為當(dāng)前線程,firstReaderHoldCount設(shè)置為1                if (r == 0) {                    firstReader = current;                    firstReaderHoldCount = 1;                } else if (firstReader == current) {                    //如果firstReader是當(dāng)前線程,則將firstReader的重入計(jì)數(shù)變量firstReaderHoldCount加1                    firstReaderHoldCount++;                } else {                    //否則說明有至少兩個(gè)線程共享讀鎖,獲取共享鎖重入計(jì)數(shù)器HoldCounter                    //從HoldCounter中拿到當(dāng)前線程的線程變量cachedHoldCounter,將此線程的重入計(jì)數(shù)count加1                    HoldCounter rh = cachedHoldCounter;                    if (rh == null || rh.tid != getThreadId(current))                        cachedHoldCounter = rh = readHolds.get();                    else if (rh.count == 0)                        readHolds.set(rh);                    rh.count++;                }                return 1;            }            //如果上面的if條件有一個(gè)都不滿足,則進(jìn)入到這個(gè)方法里進(jìn)行死循環(huán)重新獲取            return fullTryAcquireShared(current);        }        /**         * 用于處理CAS操作state失敗和tryAcquireShared中未執(zhí)行獲取可重入鎖動(dòng)作的full方法(補(bǔ)償方法?)         * Full version of acquire for reads, that handles CAS misses         * and reentrant reads not dealt with in tryAcquireShared.         */        final int fullTryAcquireShared(Thread current) {            /*             * 此代碼與tryAcquireShared中的代碼有部分相似的地方,             * 但總體上更簡(jiǎn)單,因?yàn)椴粫?huì)使tryAcquireShared與重試和延遲讀取保持計(jì)數(shù)之間的復(fù)雜判斷             * This code is in part redundant with that in             * tryAcquireShared but is simpler overall by not             * complicating tryAcquireShared with interactions between             * retries and lazily reading hold counts.             */            HoldCounter rh = null;            //死循環(huán)            for (;;) {                //獲取讀寫鎖狀態(tài)                int c = getState();                //如果有線程獲取到了寫鎖                if (exclusiveCount(c) != 0) {                    //如果獲取寫鎖的線程不是當(dāng)前線程,返回失敗                    if (getExclusiveOwnerThread() != current)                        return -1;                    // else we hold the exclusive lock; blocking here                    // would cause deadlock.                } else if (readerShouldBlock()) {//如果沒有線程獲取到寫鎖,且讀線程要阻塞                    // Make sure we're not acquiring read lock reentrantly                    //如果當(dāng)前線程為第一個(gè)獲取到讀鎖的線程                    if (firstReader == current) {                        // assert firstReaderHoldCount > 0;                    } else { //如果當(dāng)前線程不是第一個(gè)獲取到讀鎖的線程(也就是說至少有有一個(gè)線程獲取到了讀鎖)                        //                        if (rh == null) {                            rh = cachedHoldCounter;                            if (rh == null || rh.tid != getThreadId(current)) {                                rh = readHolds.get();                                if (rh.count == 0)                                    readHolds.remove();                            }                        }                        if (rh.count == 0)                            return -1;                    }                }                /**                 *下面是既沒有線程獲取寫鎖,當(dāng)前線程又不需要阻塞的情況                 */                //重入次數(shù)等于最大重入次數(shù),拋異常                if (sharedCount(c) == MAX_COUNT)                    throw new Error("Maximum lock count exceeded");                //如果執(zhí)行CAS操作成功將讀寫鎖的重入計(jì)數(shù)加1,則對(duì)當(dāng)前持有這個(gè)共享讀鎖的線程的重入計(jì)數(shù)加1,然后返回成功                if (compareAndSetState(c, c + SHARED_UNIT)) {                    if (sharedCount(c) == 0) {                        firstReader = current;                        firstReaderHoldCount = 1;                    } else if (firstReader == current) {                        firstReaderHoldCount++;                    } else {                        if (rh == null)                            rh = cachedHoldCounter;                        if (rh == null || rh.tid != getThreadId(current))                            rh = readHolds.get();                        else if (rh.count == 0)                            readHolds.set(rh);                        rh.count++;                        cachedHoldCounter = rh; // cache for release                    }                    return 1;                }            }        }

釋放讀鎖源碼:

/**  * Releases in shared mode.  Implemented by unblocking one or more  * threads if {@link #tryReleaseShared} returns true.  *  * @param arg the release argument.  This value is conveyed to  *        {@link #tryReleaseShared} but is otherwise uninterpreted  *        and can represent anything you like.  * @return the value returned from {@link #tryReleaseShared}  */public final boolean releaseShared(int arg) {    if (tryReleaseShared(arg)) {//嘗試釋放一次共享鎖計(jì)數(shù)        doReleaseShared();//真正釋放鎖        return true;    }        return false;}/** *此方法表示讀鎖線程釋放鎖。 *首先判斷當(dāng)前線程是否為第一個(gè)讀線程firstReader, *若是,則判斷第一個(gè)讀線程占有的資源數(shù)firstReaderHoldCount是否為1,  若是,則設(shè)置第一個(gè)讀線程firstReader為空,否則,將第一個(gè)讀線程占有的資源數(shù)firstReaderHoldCount減1;  若當(dāng)前線程不是第一個(gè)讀線程,  那么首先會(huì)獲取緩存計(jì)數(shù)器(上一個(gè)讀鎖線程對(duì)應(yīng)的計(jì)數(shù)器 ),  若計(jì)數(shù)器為空或者tid不等于當(dāng)前線程的tid值,則獲取當(dāng)前線程的計(jì)數(shù)器,  如果計(jì)數(shù)器的計(jì)數(shù)count小于等于1,則移除當(dāng)前線程對(duì)應(yīng)的計(jì)數(shù)器,  如果計(jì)數(shù)器的計(jì)數(shù)count小于等于0,則拋出異常,之后再減少計(jì)數(shù)即可。  無論何種情況,都會(huì)進(jìn)入死循環(huán),該循環(huán)可以確保成功設(shè)置狀態(tài)state */protected final boolean tryReleaseShared(int unused) {      // 獲取當(dāng)前線程      Thread current = Thread.currentThread();      if (firstReader == current) { // 當(dāng)前線程為第一個(gè)讀線程          // assert firstReaderHoldCount > 0;         if (firstReaderHoldCount == 1) // 讀線程占用的資源數(shù)為1              firstReader = null;          else // 減少占用的資源              firstReaderHoldCount--;     } else { // 當(dāng)前線程不為第一個(gè)讀線程         // 獲取緩存的計(jì)數(shù)器         HoldCounter rh = cachedHoldCounter;         if (rh == null || rh.tid != getThreadId(current)) // 計(jì)數(shù)器為空或者計(jì)數(shù)器的tid不為當(dāng)前正在運(yùn)行的線程的tid             // 獲取當(dāng)前線程對(duì)應(yīng)的計(jì)數(shù)器             rh = readHolds.get();         // 獲取計(jì)數(shù)         int count = rh.count;         if (count <= 1) { // 計(jì)數(shù)小于等于1             // 移除             readHolds.remove();             if (count <= 0) // 計(jì)數(shù)小于等于0,拋出異常                 throw unmatchedUnlockException();         }         // 減少計(jì)數(shù)         --rh.count;     }     for (;;) { // 死循環(huán)         // 獲取狀態(tài)         int c = getState();         // 獲取狀態(tài)         int nextc = c - SHARED_UNIT;         if (compareAndSetState(c, nextc)) // 比較并進(jìn)行設(shè)置             // Releasing the read lock has no effect on readers,             // but it may allow waiting writers to proceed if             // both read and write locks are now free.             return nextc == 0;     } } /**真正釋放鎖  * Release action for shared mode -- signals successor and ensures  * propagation. (Note: For exclusive mode, release just amounts  * to calling unparkSuccessor of head if it needs signal.)  */private void doReleaseShared() {        /*         * Ensure that a release propagates, even if there are other         * in-progress acquires/releases.  This proceeds in the usual         * way of trying to unparkSuccessor of head if it needs         * signal. But if it does not, status is set to PROPAGATE to         * ensure that upon release, propagation continues.         * Additionally, we must loop in case a new node is added         * while we are doing this. Also, unlike other uses of         * unparkSuccessor, we need to know if CAS to reset status         * fails, if so rechecking.         */        for (;;) {            Node h = head;            if (h != null && h != tail) {                int ws = h.waitStatus;                if (ws == Node.SIGNAL) {                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))                        continue;            // loop to recheck cases                    unparkSuccessor(h);                }                else if (ws == 0 &&                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))                    continue;                // loop on failed CAS            }            if (h == head)                   // loop if head changed                break;        }    }

通過分析可以看出:在線程持有讀鎖的情況下,該線程不能取得寫鎖(因?yàn)楂@取寫鎖的時(shí)候,如果發(fā)現(xiàn)當(dāng)前的讀鎖被占用,就馬上獲取失敗,不管讀鎖是不是被當(dāng)前線程持有)。在線程持有寫鎖的情況下,該線程可以繼續(xù)獲取讀鎖(獲取讀鎖時(shí)如果發(fā)現(xiàn)寫鎖被占用,只有寫鎖沒有被當(dāng)前線程占用的情況才會(huì)獲取失?。?。

LongAdder在高并發(fā)的情況下,我們對(duì)一個(gè)Integer類型的整數(shù)直接進(jìn)行i++的時(shí)候,無法保證操作的原子性,會(huì)出現(xiàn)線程安全的問題。為此我們會(huì)用juc下的AtomicInteger,它是一個(gè)提供原子操作的Interger類,內(nèi)部也是通過CAS實(shí)現(xiàn)線程安全的。但當(dāng)大量線程同時(shí)去訪問時(shí),就會(huì)因?yàn)榇罅烤€程執(zhí)行CAS操作失敗而進(jìn)行空旋轉(zhuǎn),導(dǎo)致CPU資源消耗過多,而且執(zhí)行效率也不高。Doug Lea大神應(yīng)該也不滿意,于是在JDK1.8中對(duì)CAS進(jìn)行了優(yōu)化,提供了LongAdder,它是基于了CAS分段鎖的思想實(shí)現(xiàn)的。線程去讀寫一個(gè)LongAdder類型的變量時(shí),流程如下:

java中經(jīng)典的JVM鎖有哪些

LongAdder也是基于Unsafe提供的CAS操作+valitale去實(shí)現(xiàn)的。在LongAdder的父類Striped64中維護(hù)著一個(gè)base變量和一個(gè)cell數(shù)組,當(dāng)多個(gè)線程操作一個(gè)變量的時(shí)候,先會(huì)在這個(gè)base變量上進(jìn)行cas操作,當(dāng)它發(fā)現(xiàn)線程增多的時(shí)候,就會(huì)使用cell數(shù)組。比如當(dāng)base將要更新的時(shí)候發(fā)現(xiàn)線程增多(也就是調(diào)用casBase方法更新base值失?。敲此鼤?huì)自動(dòng)使用cell數(shù)組,每一個(gè)線程對(duì)應(yīng)于一個(gè)cell,在每一個(gè)線程中對(duì)該cell進(jìn)行cas操作,這樣就可以將單一value的更新壓力分擔(dān)到多個(gè)value中去,降低單個(gè)value的 “熱度”,同時(shí)也減少了線程大量線程的空轉(zhuǎn),提高并發(fā)效率,分散并發(fā)壓力。這種分段鎖需要額外維護(hù)一個(gè)內(nèi)存空間cells,不過在高并發(fā)場(chǎng)景下,這點(diǎn)成本幾乎可以忽略。分段鎖是一種優(yōu)秀的優(yōu)化思想,juc中提供的的ConcurrentHashMap也是基于分段鎖保證讀寫操作的線程安全。

“java中經(jīng)典的JVM鎖有哪些”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

新聞標(biāo)題:java中經(jīng)典的JVM鎖有哪些
當(dāng)前地址:http://muchs.cn/article10/ihsogo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供關(guān)鍵詞優(yōu)化、網(wǎng)頁(yè)設(shè)計(jì)公司網(wǎng)站策劃、品牌網(wǎng)站制作、網(wǎng)站排名、網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

微信小程序開發(fā)