真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

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

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

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

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

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

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

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

**此處存儲的信息包括兩部分:

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

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

  • 對象類型指針(Class Pointer)

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

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

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

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

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

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

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

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

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

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

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

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

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

獲取寫鎖源碼:

/**         * 獲取寫鎖           Acquires the write lock.         *  如果此時沒有任何線程持有寫鎖或者讀鎖,那么當(dāng)前線程執(zhí)行CAS操作更新status,         *  若更新成功,則設(shè)置讀鎖重入次數(shù)為1,并立即返回         * 

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,并立即返回         * 

If the current thread already holds the write lock then the         * hold count is incremented by one and the method returns         * immediately.         *  如果該鎖已經(jīng)被另外一個線程持有,那么停止該線程的CPU調(diào)度并進(jìn)入休眠狀態(tài),         *  直到該寫鎖被釋放,且成功將寫鎖持有次數(shù)設(shè)置為1才表示獲取寫鎖成功         * 

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成功,則直接返回,代表搶鎖成功     * 否則,將會進(jìn)入同步隊列等待,不斷執(zhí)行“tryAcquire”方法嘗試CAS更新status狀態(tài),直到成功搶到鎖     * 其中“tryAcquire”方法在NonfairSync(公平鎖)中和FairSync(非公平鎖)中都有各自的實現(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、如果讀寫鎖的計數(shù)不為0,且持有鎖的線程不是當(dāng)前線程,則返回false             * 1. If read count nonzero or write count nonzero             *    and owner is a different thread, fail.             * 2、如果持有鎖的計數(shù)不為0且計數(shù)總數(shù)超過限定的最大值,也返回false             * 2. If count would saturate, fail. (This can only             *    happen if count is already nonzero.)             * 3、如果該鎖是可重入或該線程在隊列中的策略是允許它嘗試搶鎖,那么該線程就能獲取鎖             * 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");                //到這里說明該線程是重入寫鎖,更新重入寫鎖的計數(shù)(+1),返回true                // Reentrant acquire                setState(c + acquires);                return true;            }            //如果讀寫鎖狀態(tài)為0,說明讀鎖和寫鎖都沒有被獲取,會走下面兩個分支:            //如果要阻塞或者執(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();     //寫鎖的可重入計數(shù)減掉releases個     int nextc = getState() - releases;     //如果寫鎖重入計數(shù)為0了,則說明寫鎖被釋放了     boolean free = exclusiveCount(nextc) == 0;     if (free)        //若寫鎖被釋放,則將鎖的持有者設(shè)置為null,進(jìn)行GC        setExclusiveOwnerThread(null);     //更新寫鎖的重入計數(shù)     setState(nextc);     return free; }

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

獲取讀鎖源碼:

/**         * 獲取讀鎖         * Acquires the read lock.         * 如果寫鎖未被其他線程持有,執(zhí)行CAS操作更新status值,獲取讀鎖后立即返回         * 

Acquires the read lock if the write lock is not held by         * another thread and returns immediately.         *          * 如果寫鎖被其他線程持有,那么停止該線程的CPU調(diào)度并進(jìn)入休眠狀態(tài),直到該讀鎖被釋放         * 

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成功,則直接返回,代表搶鎖成功     * 否則,將會進(jìn)入同步隊列等待,不斷執(zhí)行“tryAcquireShared”方法嘗試CAS更新status狀態(tài),直到成功搶到鎖     * 其中“tryAcquireShared”方法在NonfairSync(公平鎖)中和FairSync(非公平鎖)中都有各自的實現(xiàn)     * (看這注釋是不是和寫鎖很對稱)     * 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和重入計數(shù)。             *    這里要注意的是,上面的步驟不檢查是否可重入(因為讀鎖屬于共享鎖,天生支持可重入)             * 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、如果因為CAS更新status失敗或者重入計數(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;            //獲取讀鎖的重入計數(shù)            int r = sharedCount(c);            //如果讀線程不應(yīng)該被阻塞,且重入計數(shù)小于最大值,且CAS執(zhí)行讀鎖重入計數(shù)+1成功,則執(zhí)行線程重入的計數(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的重入計數(shù)變量firstReaderHoldCount加1                    firstReaderHoldCount++;                } else {                    //否則說明有至少兩個線程共享讀鎖,獲取共享鎖重入計數(shù)器HoldCounter                    //從HoldCounter中拿到當(dāng)前線程的線程變量cachedHoldCounter,將此線程的重入計數(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條件有一個都不滿足,則進(jìn)入到這個方法里進(jìn)行死循環(huán)重新獲取            return fullTryAcquireShared(current);        }        /**         * 用于處理CAS操作state失敗和tryAcquireShared中未執(zhí)行獲取可重入鎖動作的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中的代碼有部分相似的地方,             * 但總體上更簡單,因為不會使tryAcquireShared與重試和延遲讀取保持計數(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)前線程為第一個獲取到讀鎖的線程                    if (firstReader == current) {                        // assert firstReaderHoldCount > 0;                    } else { //如果當(dāng)前線程不是第一個獲取到讀鎖的線程(也就是說至少有有一個線程獲取到了讀鎖)                        //                        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操作成功將讀寫鎖的重入計數(shù)加1,則對當(dāng)前持有這個共享讀鎖的線程的重入計數(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)) {//嘗試釋放一次共享鎖計數(shù)        doReleaseShared();//真正釋放鎖        return true;    }        return false;}/** *此方法表示讀鎖線程釋放鎖。 *首先判斷當(dāng)前線程是否為第一個讀線程firstReader, *若是,則判斷第一個讀線程占有的資源數(shù)firstReaderHoldCount是否為1,  若是,則設(shè)置第一個讀線程firstReader為空,否則,將第一個讀線程占有的資源數(shù)firstReaderHoldCount減1;  若當(dāng)前線程不是第一個讀線程,  那么首先會獲取緩存計數(shù)器(上一個讀鎖線程對應(yīng)的計數(shù)器 ),  若計數(shù)器為空或者tid不等于當(dāng)前線程的tid值,則獲取當(dāng)前線程的計數(shù)器,  如果計數(shù)器的計數(shù)count小于等于1,則移除當(dāng)前線程對應(yīng)的計數(shù)器,  如果計數(shù)器的計數(shù)count小于等于0,則拋出異常,之后再減少計數(shù)即可。  無論何種情況,都會進(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)前線程為第一個讀線程          // assert firstReaderHoldCount > 0;         if (firstReaderHoldCount == 1) // 讀線程占用的資源數(shù)為1              firstReader = null;          else // 減少占用的資源              firstReaderHoldCount--;     } else { // 當(dāng)前線程不為第一個讀線程         // 獲取緩存的計數(shù)器         HoldCounter rh = cachedHoldCounter;         if (rh == null || rh.tid != getThreadId(current)) // 計數(shù)器為空或者計數(shù)器的tid不為當(dāng)前正在運(yùn)行的線程的tid             // 獲取當(dāng)前線程對應(yīng)的計數(shù)器             rh = readHolds.get();         // 獲取計數(shù)         int count = rh.count;         if (count <= 1) { // 計數(shù)小于等于1             // 移除             readHolds.remove();             if (count <= 0) // 計數(shù)小于等于0,拋出異常                 throw unmatchedUnlockException();         }         // 減少計數(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;        }    }

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

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

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

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

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


文章標(biāo)題:java中經(jīng)典的JVM鎖有哪些
鏈接URL:http://weahome.cn/article/ihsogo.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部