juc-locks

juc-locks 鎖框架

java提供各類鎖的一個框架

鎖的性質劃分

多個線程是否按順序獲取鎖的角度劃分--公平鎖與非公平鎖

\color{red}{公平鎖}:當鎖被某個線程持有時蔫劣,新發(fā)出請求的線程會被放入隊列中昌腰,在公平鎖中渐苏,每一次嘗試獲取鎖都會檢查CLH隊列中是否仍有前驅的元素傲茄,如果仍然有那么繼續(xù)等待

\color{red}{非公平鎖}:非公平鎖和公平鎖在獲取鎖的方法上困乒,流程是一樣的迷捧;它們的區(qū)別主要表現(xiàn)在“嘗試獲取鎖的機制不同”军拟,無視等待隊列直接嘗試獲取鎖补鼻,如果鎖是空閑的哄啄,即可獲取狀態(tài),則獲取鎖

\color{red}{優(yōu)缺點}:公平鎖為了保證線程排隊风范,需要增加阻塞和喚醒的時間開銷咨跌,非公平鎖是可以減少喚起線程的開銷,缺點是處于等待隊列中的線程可能會出現(xiàn)一直獲取不到鎖的現(xiàn)象(餓死)乌企,如果線程處理時間過長虑润,一部分客戶端獲取不到服務(違反公平原則,一直被新的線程占用cpu)加酵,此時可以使用公平鎖拳喻,否則使用非公平鎖提供吞吐量

\color{red}{饑餓原因}
高優(yōu)先級線程占用所有CPU,低優(yōu)先級線程一致無法獲取
一個線程每次競爭鎖都失敗猪腕, 而且新的線程還在一直不斷競爭冗澈,從而導致這個線程就幾乎是一直處于等待中
某個線程在某個對象的條件隊列上等待,而其他線程不斷的搶入

多個線程是否可以持有同一把鎖的角度劃分--獨享鎖與共享鎖

\color{red}{獨享鎖(互斥鎖/寫鎖)}:是指該鎖一次只能被一個線程所持有

\color{red}{共享鎖(讀鎖)}:是指該鎖可被多個線程所持有

一個線程能否重復獲取自己的鎖的角度劃分--重入鎖(遞歸鎖)與不重入鎖

\color{red}{重入鎖}:同一個線程而言陋葡,它可以重復的獲取鎖亚亲,避免同一個線程重復獲取鎖發(fā)生死鎖

\color{red}{不重入鎖}:一個線程多次請求同一把鎖,會出現(xiàn)死鎖

\color{red}{優(yōu)缺點}:重入鎖和不可重入鎖主要的差別在對相同線程是否能夠重復獲取腐缤,從效率來說捌归,不可重入鎖效率更高

鎖的設計方案來分類

多個線程競爭同一把鎖是否進行阻塞劃分--自旋鎖與自適應自旋鎖

\color{red}{自旋鎖(spinlock)}:指當有另外一個線程來競爭鎖時,這個線程會在原地循環(huán)等待岭粤,而不是把該線程給阻塞惜索,直到那個獲得鎖的線程釋放鎖之后,這個線程就可以馬上獲得鎖的

\color{red}{自適應自旋鎖}:自適應意味著自旋的時間(次數)不再固定剃浇,而是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態(tài)來決定巾兆,如果在同一個鎖對象上猎物,自旋等待剛剛成功獲得過鎖,并且持有鎖的線程正在運行中角塑,那么虛擬機就會認為這次自旋也是很有可能再次成功蔫磨,進而它將允許自旋等待持續(xù)相對更長的時間。如果對于某個鎖圃伶,自旋很少成功獲得過堤如,那在以后嘗試獲取這個鎖時將可能省略掉自旋過程,直接阻塞線程留攒,避免浪費處理器資源

\color{red}{優(yōu)缺點}:自旋鎖是一種非阻塞鎖煤惩,如果某線程需要獲取自旋鎖,但該鎖已經被其他線程占用時炼邀,該線程不會被掛起魄揉,而是在不斷的消耗CPU的時間,不停的試圖獲取自旋鎖拭宁,自旋鎖不會使線程狀態(tài)發(fā)生切換執(zhí)行速度快洛退,但是會不斷消耗CPU

多個線程并發(fā)時鎖的粒度以及鎖競爭劃分--分段鎖

\color{red}{分段鎖}:在鎖發(fā)生競爭使用獨享鎖保護受限資源時,一次只能有一個線程能訪問獨享鎖杰标,可以采取一種機制來協(xié)調獨享鎖來提高并發(fā)性兵怯,這個機制就是分段鎖,這項技術使得ConcurrentHashMap(JDK1.7)支持多達16個并發(fā)的寫入操作

\color{red}{優(yōu)缺點}:與單個鎖來相比腔剂,獲取多個鎖來實現(xiàn)獨占訪問將更加困難并且開銷更高

多個線程寫操作干擾本身線程的程度劃分--樂觀鎖/悲觀鎖

\color{red}{樂觀鎖}:認為每次去讀數據時媒区,不會進行其他寫操作,采用CSA
\color{red}{悲觀鎖}:認為每次去讀數據時掸犬,都有寫操作袜漩,類似synchronized

\color{red}{優(yōu)缺點}:悲觀鎖是指當一個線程獲取鎖時其他線程只能進行阻塞,并且進程掛起和恢復執(zhí)行過程中也存在著很大的開銷湾碎,CAS是項樂觀鎖技術宙攻,當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值介褥,而其它線程都失敗座掘,失敗的線程并不會被掛起,而是被告知這次競爭中失敗柔滔,并可以再次嘗試溢陪,使用樂觀鎖如果存在大量的寫操作,需要不斷重試睛廊,浪費CPU資源

多個線程競爭程度劃分--偏向鎖/輕量鎖/重量鎖

\color{red}{偏向鎖}:只有一個申請鎖的線程使用鎖
\color{red}{輕量鎖}:多個線程交替使用鎖嬉愧,允許短時間的鎖競爭
\color{red}{重量鎖}:有實際競爭,且鎖競爭時間長

\color{red}{優(yōu)缺點}:如果一個同步方法喉前,沒有多線程競爭没酣,并且總是由同一個線程多次獲取鎖,如果每次還有阻塞線程,那么對于CPU是一種資源的浪費,為了解決這類問題卵迂,就誕生了偏向鎖(偏向鎖只需要在置換ThreadID的時候依賴一次CAS原子指令裕便,因為只有一個線程在競爭,我們只要去判斷該偏向鎖中的ThreadID是否為當前線程即可)见咒,輕量級鎖是通過CAS來避免進入開銷較大的互斥操作偿衰,CAS是無鎖的可以避免線程狀態(tài)切換帶來的開銷,但是不適合鎖競爭時間長(線程計算慢)改览,非常浪費CPU資源下翎,重量級鎖更適合時間長的情況下使用,可以避免CPU浪費

Lock

為了解決synchronized阻塞帶來的性能問題宝当,JDK1.5提供了線程同步工具Lock接口方便實現(xiàn)各類鎖视事,從Lock提供的各類鎖的角度來說,對比synchronized這種悲觀鎖要顯得更加豐富和靈活

synchronized不具備的特性 描述
嘗試鎖 判斷線程狀態(tài)是否獲取了鎖庆揩,獲得返回true否則false
中斷鎖 線程獲得鎖繼續(xù)執(zhí)行俐东,鎖不可用則阻塞,阻塞情況下其他線程獲得鎖則拋出異常
超時鎖 線程超時未獲取鎖订晌,返回false否則true
釋放鎖 需要手動釋放鎖虏辫,不然造成死鎖
Lock API 描述
lock() 獲得鎖,否則一直等待
unlock() 釋放鎖
tryLock() 判斷鎖狀態(tài)锈拨,鎖被占用就返回false砌庄,否則返回true
tryLock(long time, TimeUnit unit) 比起tryLock()添加一個時間期限判斷
lockInterruptibly() 此種獲取鎖的方式,鎖被占用的情況下拋出異常奕枢,直接中斷娄昆,可以去做其他處理
Condition newCondition() 通知組件:具有阻塞與喚醒功能

Condition

Object類中的wait()、notify()验辞、notifyAll()方法實現(xiàn)了線程之間的通信稿黄,而Condition類中的await()、signal()跌造、signalAll()方法也實現(xiàn)了相似的功能杆怕。通過Condition能夠精細的控制多線程的休眠與喚醒, 對于一個鎖壳贪,我們可以為多個線程間建立不同的Condition陵珍。ConditionObject是同步器AbstractQueuedSynchronizer的內部類,每個Condition對象都包含著一個隊列违施,該隊列是Condition對象實現(xiàn)等待/通知功能的關鍵

\color{red}{同步隊列}:AQS的同步排隊用了一個隱式的雙向隊列互纯,同步隊列的每個節(jié)點是一個AbstractQueuedSynchronizer.Node實例

\color{red}{等待隊列}:Condition的等待隊列是一個隱式的單向隊列,等待隊列中的每一個節(jié)點也是一個AbstractQueuedSynchronizer.Node實例

\color{red}{總結}
1:整個過程是節(jié)點在同步隊列和等待隊列中來回移動實現(xiàn)的
2:每當一個線程調用Condition.await()方法磕蒲,那么該線程會釋放鎖留潦,構造成一個Node節(jié)點加入到等待隊列的隊尾只盹,自旋判斷如果當前節(jié)點沒有在同步隊列上則將當前線程阻塞
3:調用Condition的signal()方法,會將節(jié)點移到同步隊列中兔院,但不一定在隊首
4:如果退出自旋說明當前節(jié)點已經在同步隊列上殖卑。通過acquireQueued將阻塞直到當前節(jié)點成為隊首,即當前線程獲得了鎖坊萝。然后await()方法就可以退出了孵稽,讓線程繼續(xù)執(zhí)行await()后的代碼

\color{red}{參考資料}
https://www.cnblogs.com/sheeva/p/6484224.html

AbstractQueuedSynchronizer

AQS提供一個框架,一個FOFO的等待隊列和一個代表狀態(tài)的int值十偶,子類需要定義這個狀態(tài)的protected方法菩鲜,定義什么狀態(tài)獲取到狀態(tài)以及釋放鎖狀態(tài),該類方法提供所有入隊和阻塞機制惦积,AQS框架提供了一套通用的機制來管理同步狀態(tài)接校、阻塞/喚醒線程、管理等待隊列,是JUC上大多數同步器的基礎

\color{red}{模板模式}:定義一個操作中算法的骨架荣刑,而將一些步驟延遲到子類中馅笙,模板方法使得子類可以不改變算法的結構即可重定義該算法的某些特定步驟

\color{red}{模板方法}:AQS使用了一組模板方法,直接調用\color{red}{同步組件方法}

AbstractQueuedSynchronizer的模板方法 描述
void acquire(int arg) 會調用tryAcquire方法厉亏,如果未獲取成功董习,則會進入同步隊列,通過死循環(huán)爱只,直到node節(jié)點的線程獲取到鎖皿淋,才返回
void acquireInterruptibly(int arg) 會調用tryAcquire方法,如果未獲取成功恬试,則會進入同步隊列窝趣,通過死循環(huán),直到node節(jié)點的線程獲取到鎖训柴,才返回哑舒,當外界對當前線程進行中斷的時候提前結束獲取狀態(tài)的操作
boolean tryAcquireNanos(int arg,long nanos) acquireInterruptibly方法的升級版,也就是在判斷是否被中斷的基礎上增加了超時控制
void acquireShared(int arg) 調用tryAcquireShared方法嘗試獲取共享鎖幻馁,如果返回值大于0表示獲取共享鎖成功直接返回洗鸵,否則入隊阻塞,一直循環(huán)直到tryAcquireShared方法獲取共享鎖成功仗嗦,如果共享狀態(tài)獲取成功之后會判斷后繼節(jié)點的狀態(tài)是否Node.SIGNAL膘滨,如果是共享模式,那么就直接對其進行喚醒操作稀拐,也就是同時激發(fā)多個線程并發(fā)的運行
void acquireSharedInterruptibly(int arg) acquireShared方法的基礎上添加了中斷判斷火邓,當外界對當前線程進行中斷的時候提前結束獲取狀態(tài)的操作
boolean tryAcquireSharedNanos(int arg,long nanos) acquireSharedInterruptibly方法的升級版,也就是在判斷是否被中斷的基礎上增加了超時控制
boolean release(int arg) 調用tryRelease方法釋放當前持有的鎖資源返回true,則判斷隊列頭節(jié)點的狀態(tài)不是0或者不是null則喚醒一個線程
boolean releaseShared(int arg) 調用tryReleaseShared釋放鎖如果返回true铲咨,則喚醒后續(xù)節(jié)點
Collection getQueuedThreads() 獲取同步隊列上的線程集合

\color{red}{同步組件}:程序員需要選擇覆蓋實現(xiàn)以下方法來實現(xiàn)同步狀態(tài)的管理

自定義的同步組件 描述
boolean isHeldExclusively() 判斷線程狀態(tài)是否獲取了鎖
boolean tryAcquire(int arg) 獨占式獲取同步狀態(tài)
boolean tryRelease(int arg) 獨占式釋放同步狀態(tài)
int tryAcquireShared(int arg) 共享式獲取同步狀態(tài)
boolean tryReleaseShared(int arg) 共享式私房同步狀態(tài)

\color{red}{同步狀態(tài)}:同步狀態(tài)的管理配合同步組件使用

同步狀態(tài) 描述
int getState() 獲取同步狀態(tài)
void setState() 設置同步狀態(tài)躲胳,必須保證線程是安全的
boolean compareAndSetState(int expect, int update) CAS 設置同步狀態(tài)

\color{red}{CLH同步隊列}:CLH同步隊列是一個FIFO雙向隊列,AQS依賴它來完成同步狀態(tài)的管理纤勒,在CLH同步隊列中泛鸟,一個節(jié)點表示一個線程

\color{red}{總結}:線程會首先嘗試獲取鎖,失敗則將當前線程以及等待狀態(tài)等信息包成一個Node節(jié)點加到CLH隊列來管理鎖踊东,接著通過死循環(huán)嘗試獲取鎖,在拿到鎖為止會不斷阻塞刚操,等到線程釋放鎖時闸翅,會喚醒隊列中的后繼線程

\color{red}{參考資料}http://www.reibang.com/p/0da2939391cf

ReentrantLock

ReentrantLock 描述
ReentrantLock(boolean fair) 創(chuàng)建一個(公平/非公平)的具有可重入性質的實例對象

\color{red}{Sync}:Sync為ReentrantLock的成員變量,Sync的子類分別為FairSync和NonfairSync

非公平鎖
static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 8424253563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
    
final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

1:通過CSA加鎖菊霜,如果超成功則setExclusiveOwnerThread(設置獨占鎖)坚冀,如果不成功代表以及持有鎖,是一個重入的線程
2:acquire為AbstractQueuedSynchronizer的模板方法鉴逞,獲取線程狀態(tài)并調用compareAndSetState進行設置(加鎖)记某,加鎖失敗代表鎖已經被占用,
則判斷當前線程是否鎖的持有者构捡,如果是則代表是一把重入鎖液南。通過setState不斷累加鎖重入的次數

釋放鎖
protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

如果當前線程不是鎖的持有者,拋出異常勾徽,鎖記錄為0代表釋放全部滑凉,返回true,否則設置同步狀態(tài)返回false

公平鎖
static final class FairSync extends Sync {
        private static final long serialVersionUID = -782823691563782823376L;

        final void lock() {
            acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
           final Thread current = Thread.currentThread();
           int c = getState();
           if (c == 0) {
               if (!hasQueuedPredecessors() &&
                   compareAndSetState(0, acquires)) {
                   setExclusiveOwnerThread(current);
                   return true;
               }
           }
           else if (current == getExclusiveOwnerThread()) {
               int nextc = c + acquires;
               if (nextc < 0)
                   throw new Error("Maximum lock count exceeded");
               setState(nextc);
               return true;
           }
           return false;
        }
    }

調用hasQueuedPredecessors判斷:當前線程不是同步隊列有其他線程優(yōu)先則返回false不能獲取鎖

ReentrantReadWriteLock

ReentrantReadWriteLock特性 描述
公平與非公平 多個線程是否按順序獲取鎖的角度
重入 同一個線程它可以重復的獲取鎖
鎖降級 同一個線程中沒有釋放寫鎖的情況下喘帚,就去申請讀鎖屬于鎖降級
public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
    private static final long serialVersionUID = -6992448646407690164L;
    /** Inner class providing readlock */
    private final ReentrantReadWriteLock.ReadLock readerLock;
    /** Inner class providing writelock */
    private final ReentrantReadWriteLock.WriteLock writerLock;
    /** Performs all synchronization mechanics */
    final Sync sync;
}

public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
    private static final long serialVersionUID = -6992448646407690164L;
    /** Inner class providing readlock */
    private final ReentrantReadWriteLock.ReadLock readerLock;
    /** Inner class providing writelock */
    private final ReentrantReadWriteLock.WriteLock writerLock;
    /** Performs all synchronization mechanics */
    final Sync sync;
}

 /**
     * Synchronization implementation for ReentrantReadWriteLock.
     * Subclassed into fair and nonfair versions.
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 6317671515068378041L;

        /*
         * Read vs write count extraction constants and functions.
         * Lock state is logically divided into two unsigned shorts:
         * The lower one representing the exclusive (writer) lock hold count,
         * and the upper the shared (reader) hold count.
         */

        static final int SHARED_SHIFT   = 16;
        static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
        static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
        static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;

        /** Returns the number of shared holds represented in count  */
        static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
        /** Returns the number of exclusive holds represented in count  */
        static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
}

public interface ReadWriteLock {
    /**
     * Returns the lock used for reading.
     *
     * @return the lock used for reading.
     */
    Lock readLock();
 
    /**
     * Returns the lock used for writing.
     *
     * @return the lock used for writing.
     */
    Lock writeLock();
}

讀寫鎖是依賴AQS框架實現(xiàn)的共享鎖與排它鎖畅姊,AQS 維護一個state是32位的,內部類Sync采用一套運算規(guī)則吹由,實現(xiàn)了高位保存共享鎖若未,低位保存獨占鎖的一套邏輯

public static class ReadLock implements Lock, java.io.Serializable {
        private static final long serialVersionUID = -5992448646407690164L;
        private final Sync sync;

        /**
         * Constructor for use by subclasses
         *
         * @param lock the outer lock object
         * @throws NullPointerException if the lock is null
         */
        protected ReadLock(ReentrantReadWriteLock lock) {
            sync = lock.sync;
        }

        /**
         * Acquires the read lock.
         *
         * <p>Acquires the read lock if the write lock is not held by
         * another thread and returns immediately.
         *
         * <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);
        }
        
        public void unlock() {
            sync.releaseShared(1);
        }

}

public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }
    
protected final int tryAcquireShared(int unused) {
            /*
             * Walkthrough:
             * 1. If write lock held by another thread, fail.
             * 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. If step 2 fails either because thread
             *    apparently not eligible or CAS fails or count
             *    saturated, chain to version with full retry loop.
             */
            Thread current = Thread.currentThread();
            int c = getState();
            //exclusiveCount(c) --計數高低位 
            //exclusiveCount(c) != 0 --代表寫鎖
            //getExclusiveOwnerThread() != current -- 當前線程不是鎖的持有者
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
            int r = sharedCount(c);
          // 如果readerShouldBlock()返回false,且獲取次數r小于MAX_COUNT倾鲫,且設置state狀態(tài)成功
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                compareAndSetState(c, c + SHARED_UNIT)) {
                //r==0表示第一個線程
                if (r == 0) {
                    firstReader = current;   //將線程設置為 第一個獲取讀鎖的線程
                    firstReaderHoldCount = 1;
                } else if (firstReader == current) {   
                 // 如果線程等于 第一個獲取讀鎖的線程 則代表重入 粗合,進行累加
                    firstReaderHoldCount++;
                } else {          //代表有其他線程競爭,利用HoldCounter累加
                    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;
            }
            return fullTryAcquireShared(current); //嘗試獲取讀鎖
        }
        

 final int fullTryAcquireShared(Thread current) {   
            /*
             * 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;
            for (;;) {
                int c = getState();
                if (exclusiveCount(c) != 0) {
                    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
                    if (firstReader == current) {
                        // assert firstReaderHoldCount > 0;
                    } else {
                        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;
                    }
                }
                if (sharedCount(c) == MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                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;
                }
            }
        }
/**
     * Fair version of Sync
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -2274990926593161451L;
        final boolean writerShouldBlock() {
            return hasQueuedPredecessors();
        }
        final boolean readerShouldBlock() {
            return hasQueuedPredecessors();
        }
    }
    /**
     * Nonfair version of Sync
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -8159625535654395037L;
        final boolean writerShouldBlock() {
            return false; // writers can always barge
        }
        final boolean readerShouldBlock() {
            /* As a heuristic to avoid indefinite writer starvation,
             * block if the thread that momentarily appears to be head
             * of queue, if one exists, is a waiting writer.  This is
             * only a probabilistic effect since a new reader will not
             * block if there is a waiting writer behind other enabled
             * readers that have not yet drained from the queue.
             */
            return apparentlyFirstQueuedIsExclusive();
        }
    }

apparentlyFirstQueuedIsExclusive:只有當CLH隊列第一個節(jié)點處于獨占鎖模式才返回true
hasQueuedPredecessors返回true级乍,表示在CLH隊列中還有其他線程在當前線程前面

static final class HoldCounter {
            int count = 0;
            // Use id, not reference, to avoid garbage retention
            final long tid = getThreadId(Thread.currentThread());
        }

        /**
         * ThreadLocal subclass. Easiest to explicitly define for sake
         * of deserialization mechanics.
         */
        static final class ThreadLocalHoldCounter
            extends ThreadLocal<HoldCounter> {
            public HoldCounter initialValue() {
                return new HoldCounter();
            }
        }

        /**
         * The number of reentrant read locks held by current thread.
         * Initialized only in constructor and readObject.
         * Removed whenever a thread's read hold count drops to 0.
         */
        private transient ThreadLocalHoldCounter readHolds;

        /**
         * The hold count of the last thread to successfully acquire
         * readLock. This saves ThreadLocal lookup in the common case
         * where the next thread to release is the last one to
         * acquire. This is non-volatile since it is just used
         * as a heuristic, and would be great for threads to cache.
         *
         * <p>Can outlive the Thread for which it is caching the read
         * hold count, but avoids garbage retention by not retaining a
         * reference to the Thread.
         *
         * <p>Accessed via a benign data race; relies on the memory
         * model's final field and out-of-thin-air guarantees.
         */
        private transient HoldCounter cachedHoldCounter;

        /**
         * firstReader is the first thread to have acquired the read lock.
         * firstReaderHoldCount is firstReader's hold count.
         *
         * <p>More precisely, firstReader is the unique thread that last
         * changed the shared count from 0 to 1, and has not released the
         * read lock since then; null if there is no such thread.
         *
         * <p>Cannot cause garbage retention unless the thread terminated
         * without relinquishing its read locks, since tryReleaseShared
         * sets it to null.
         *
         * <p>Accessed via a benign data race; relies on the memory
         * model's out-of-thin-air guarantees for references.
         *
         * <p>This allows tracking of read holds for uncontended read
         * locks to be very cheap.
         */
        private transient Thread firstReader = null;
        private transient int firstReaderHoldCount;

        Sync() {
            readHolds = new ThreadLocalHoldCounter();
            setState(getState()); // ensures visibility of readHolds
        }

計算讀鎖次數利用ThreadLocal為每一個線程維護一份獨立的變量

protected final boolean tryReleaseShared(int unused) {
            Thread current = Thread.currentThread();
            if (firstReader == current) {
                // assert firstReaderHoldCount > 0;
                if (firstReaderHoldCount == 1)
                    firstReader = null;
                else
                    firstReaderHoldCount--;
            } else {
                HoldCounter rh = cachedHoldCounter;
                if (rh == null || rh.tid != getThreadId(current))
                    rh = readHolds.get();
                int count = rh.count;
                if (count <= 1) {
                    readHolds.remove();
                    if (count <= 0)
                        throw unmatchedUnlockException();
                }
                --rh.count;
            }
            for (;;) {
                int c = getState();
                int nextc = c - SHARED_UNIT;
                if (compareAndSetState(c, nextc))
                    // 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;
            }
        }

讀鎖的獲取與釋放過程中都用到了HoldCounter類,HoldCounter類主要起著計數器的作用舌劳,對讀鎖的獲取與釋放操作會更新對應的計數值。若線程獲取讀鎖玫荣,則該計數器+1甚淡,釋放讀鎖,該計數器-1

protected final boolean tryAcquire(int acquires) {
            /*
             * Walkthrough:
             * 1. If read count nonzero or write count nonzero
             *    and owner is a different thread, fail.
             * 2. If count would saturate, fail. (This can only
             *    happen if count is already nonzero.)
             * 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();
            int c = getState();
            int w = exclusiveCount(c);
            if (c != 0) {
                // (Note: if c != 0 and w == 0 then shared count != 0)
                //w == 0表示寫鎖未獲取
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // Reentrant acquire
                setState(c + acquires);
                return 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) {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            int nextc = getState() - releases;
            boolean free = exclusiveCount(nextc) == 0;
            if (free)
                setExclusiveOwnerThread(null);
            setState(nextc);
            return free;
        }

1:c != 0表示已經獲取鎖,判斷當前線程是否鎖的持有者是否獲取寫鎖贯卦,是否小于寫次數资柔,都滿足條件進行累加,否則進行阻塞嘗試獲取
2:計算state值撵割,不斷進行累減贿堰,直到==0,代表釋放鎖成功

//JDK提供的鎖降級的一個例子
class CachedData {
    Object data;
    volatile boolean cacheValid;
    final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
 
    void processCachedData() {
      rwl.readLock().lock();
      if (!cacheValid) {
        // Must release read lock before acquiring write lock
        rwl.readLock().unlock();
        rwl.writeLock().lock();
        try {
          // Recheck state because another thread might have
          // acquired write lock and changed state before we did.
          if (!cacheValid) {
            data = ...
            cacheValid = true;
          }
          // Downgrade by acquiring read lock before releasing write lock
          rwl.readLock().lock();
        } finally {
          rwl.writeLock().unlock(); // Unlock write, still hold read
        }
      }
 
      try {
        use(data);
      } finally {
        rwl.readLock().unlock();
      }
    }

LockSupport

Object類的wait/notify機制相比啡彬,park/unpark有兩個優(yōu)點:1. 以thread為操作對象更符合阻塞線程的直觀定義羹与;2. 操作更精準,可以準確地喚醒某一個線程

每個線程都有一個許可(permit)庶灿,permit只有兩個值1和0,默認是0,通過park/unpark設置

StampedLock

為什么有了ReentrantReadWriteLock纵搁,還在JDK1.8時引入StampedLock?

讀操作一直都能搶占到CPU時間片往踢,而寫操作一直搶不了腾誉,可能導致寫的饑餓問題,正因為ReentrantReadWriteLock出現(xiàn)了讀和寫是互斥的情況峻呕,這個地方需要優(yōu)化利职,因此就出現(xiàn)了StampedLock

/**
     * Returns a stamp that can later be validated, or zero
     * if exclusively locked.
     *
     * @return a stamp, or zero if exclusively locked
     */
    public long tryOptimisticRead() {
        long s;
        return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L;
    }

    /**
     * Returns true if the lock has not been exclusively acquired
     * since issuance of the given stamp. Always returns false if the
     * stamp is zero. Always returns true if the stamp represents a
     * currently held lock. Invoking this method with a value not
     * obtained from {@link #tryOptimisticRead} or a locking method
     * for this lock has no defined effect or result.
     *
     * @param stamp a stamp
     * @return {@code true} if the lock has not been exclusively acquired
     * since issuance of the given stamp; else false
     */
    public boolean validate(long stamp) {
        U.loadFence();
        return (stamp & SBITS) == (state & SBITS);
    }

tryOptimisticRead():當前沒有線程持有寫鎖,則簡單的返回一個非 0 的 stamp 版本信息
validate():檢測樂觀讀版本號是否變化

最后

本人做的一些學習總結瘦癌,期間也參考了很多大師兄的資料猪贪,目前還有一些地方理解不到位,如果某些地方描述不夠或者錯誤佩憾,還請各位大兄弟給予不同意見

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末哮伟,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子妄帘,更是在濱河造成了極大的恐慌楞黄,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抡驼,死亡現(xiàn)場離奇詭異鬼廓,居然都是意外死亡,警方通過查閱死者的電腦和手機致盟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門碎税,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人馏锡,你說我怎么就攤上這事雷蹂。” “怎么了杯道?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵匪煌,是天一觀的道長。 經常有香客問我,道長萎庭,這世上最難降的妖魔是什么霜医? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮驳规,結果婚禮上肴敛,老公的妹妹穿的比我還像新娘。我一直安慰自己吗购,他們只是感情好医男,可當我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著捻勉,像睡著了一般昨登。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贯底,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天,我揣著相機與錄音撒强,去河邊找鬼禽捆。 笑死,一個胖子當著我的面吹牛飘哨,可吹牛的內容都是我干的胚想。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼芽隆,長吁一口氣:“原來是場噩夢啊……” “哼浊服!你這毒婦竟也來了?” 一聲冷哼從身側響起胚吁,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤牙躺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后腕扶,有當地人在樹林里發(fā)現(xiàn)了一具尸體孽拷,經...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年半抱,在試婚紗的時候發(fā)現(xiàn)自己被綠了脓恕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡窿侈,死狀恐怖炼幔,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情史简,我是刑警寧澤乃秀,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響环形,放射性物質發(fā)生泄漏策泣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一抬吟、第九天 我趴在偏房一處隱蔽的房頂上張望萨咕。 院中可真熱鬧,春花似錦火本、人聲如沸危队。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茫陆。三九已至,卻和暖如春擎析,著一層夾襖步出監(jiān)牢的瞬間簿盅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工揍魂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留桨醋,地道東北人。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓现斋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親庄蹋。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,647評論 2 354

推薦閱讀更多精彩內容