java鎖(7)改進讀寫鎖StampedLock詳解

1、StampedLock特性

StampedLock是JDK 8新增的讀寫鎖,跟讀寫鎖不同,它並不是由AQS實現的。它的state爲一個long型變量,狀態的設計也不同於讀寫鎖,且提供了三種模式來控制 read/write 的獲取,並且內部實現了自己的同步等待隊列。

1.1、StampedLock讀寫鎖

寫鎖:使用writeLock方法獲取,當鎖不可用時會阻塞,獲取成功後返回一個與這個寫鎖對應的stamp,在unlockWrite方法中,需要通過這個stamp來釋放與之對應的鎖。在tryWriteLock同樣也會提供這個stamp。當在write模式中獲取到寫鎖時,讀鎖不能被獲取,並且所有的樂觀讀鎖驗證(validate方法)都會失敗。
讀鎖:使用readLock方法獲取,當超出可用資源時(類似AQS的state設計)會阻塞。同樣的,在獲取鎖成功後也會返回stamp,作用與上述相同。tryReadLock同樣如此。
樂觀讀鎖:使用tryOptimisticRead方法獲取,只有在寫鎖可用時才能成功獲取樂觀讀鎖,獲取成功後也會返回一個stamp。validate方法可以根據這個stamp來判斷寫鎖是否被獲取。這種模式可以理解爲一個弱化的讀鎖(weak version of a read-lock),它在任何時候都能被破壞。樂觀讀模式常被用在短的只讀的代碼段,用來減少爭用並提高吞吐量。樂觀讀區域應該只讀取字段,並將它們保存在本地變量中,以便在驗證(validate方法)後使用。在樂觀讀模式中字段的讀取可能會不一致,所以可能需要反覆調用validate()來檢查一致性。例如,當首次讀取一個對象或數組引用,然後訪問其中一個的字段、元素或方法時,這些步驟通常是必需的。

1.2、三種模式的轉換

StampedLock可以將三種模式是鎖進行有條件的互相轉換。
將其他鎖轉換爲寫鎖tryConvertToWriteLock():
當前郵戳爲持有寫鎖模式,直接返回當前的郵戳;
當前郵戳爲持有讀鎖模式,則會釋放讀鎖並獲取寫鎖,並返回寫鎖郵戳;
當前郵戳持有樂觀鎖,通過CAS立即獲取寫鎖,成功則返回寫鎖郵戳;失敗則返回0;

將其他鎖轉換爲讀鎖tryConvertToReadLock:
當前郵戳爲持有寫鎖模式,則會釋放寫鎖並獲取讀鎖,並返回讀鎖郵戳;
當前郵戳爲持有讀鎖模式,則直接返回當前讀鎖郵戳;
當前郵戳持有樂觀鎖,通過CAS立即獲取讀鎖,則返回讀鎖郵戳;否則,獲取失敗返回0;

將其他鎖轉換爲樂觀鎖tryConvertToOptimisticRead:
當前郵戳爲持有讀或寫鎖,則直接釋放讀寫鎖,並返回釋放後的觀察者郵戳值;
當前郵戳持有樂觀鎖,若樂觀鎖郵戳有效,則返回觀察者郵戳;

1.3、StampedLock的應用場景

StampedLock一般作爲線程安全的內部工具類。它的使用依賴於對數據、對象和方法的內部屬性有一定的瞭解。StampedLock 是不可重入的,所以在鎖的內部不能調用其他嘗試重複獲取鎖的方。一個stamp如果在很長時間都沒有使用或驗證,在很長一段時間之後可能就會驗證失敗。StampedLocks是可序列化的,但是反序列化後變爲初始的非鎖定狀態,所以在遠程鎖定中是不安全的。

1.4、StampedLock的公平性

StampedLock 的調度策略不會始終偏向讀線程或寫線程,所有的"try"方法都是盡最大努力獲取,並不一定遵循任何調度或公平策略。從"try"方法獲取或轉換鎖失敗返回0時,不會攜帶任何鎖的狀態信息。由於StampedLock支持跨多個鎖模式的協調使用,它不會直接實現Lock或ReadWriteLock接口。但是,如果應用程序需要Lock的相關功能,它可以通過asReadLock()、asWriteLock()和asReadWriteLock()方法返回一個Lock視圖。

2、源碼分析

2.1、主要屬性

//獲取CPU的可用線程數量,用於確定自旋的時候循環次數
private static final int NCPU = Runtime.getRuntime().availableProcessors();

//根據NCPU確定自旋的次數限制(並不是一定這麼多次,因爲實際代碼中是隨機的)
private static final int SPINS = (NCPU > 1) ? 1 << 6 : 0;

//頭節點上的自旋次數
private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 0;

//頭節點上的最大自旋次數
private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 0;

//等待自旋鎖溢出的週期數
private static final int OVERFLOW_YIELD_RATE = 7; // must be power 2 - 1

//在溢出之前讀線程計數用到的bit位數
private static final int LG_READERS = 7;

//一個讀狀態單位:0000 0000 0001
private static final long RUNIT = 1L;
//一個寫狀態單位:0000 1000 0000
private static final long WBIT  = 1L << LG_READERS;
//讀狀態標識:0000 0111 1111
private static final long RBITS = WBIT - 1L;
//讀鎖計數的最大值:0000 0111 1110
private static final long RFULL = RBITS - 1L;
//讀線程個數和寫線程個數的掩碼:0000 1111 1111
private static final long ABITS = RBITS | WBIT;
//// 讀線程個數的反數,高25位全部爲1:1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1000 0000
private static final long SBITS = ~RBITS; // note overlap with ABITS

// state的初始值
private static final long ORIGIN = WBIT << 1;

// 中斷標識
private static final long INTERRUPTED = 1L;

// 節點狀態值,等待/取消
private static final int WAITING   = -1;
private static final int CANCELLED =  1;

// 節點模式,讀模式/寫模式
private static final int RMODE = 0;
private static final int WMODE = 1;


//等待隊列的頭節點
private transient volatile WNode whead;
//等待隊列的尾節點
private transient volatile WNode wtail;

// 鎖狀態
private transient volatile long state;
////因爲讀狀態只有7位很小,所以當超過了128之後將使用此變量來記錄
private transient int readerOverflow;

2.2、node節點實現

//等待隊列的節點實現
static final class WNode {
    //前驅節點
    volatile WNode prev;
    //後繼節點
    volatile WNode next;
    //讀線程使用的鏈表
    volatile WNode cowait;    // list of linked readers
    //等待的線程
    volatile Thread thread;   // non-null while possibly parked
    //節點狀態
    volatile int status;      // 0, WAITING, or CANCELLED
    //節點模式
    final int mode;           // RMODE or WMODE
    WNode(int m, WNode p) { mode = m; prev = p; }
}

2.3、state狀態實現

state狀態說明:

  • bit0—bit6爲作爲讀鎖計數,當超出RFULL(126)時,用readerOverflow作爲讀鎖計數。當獲取到讀鎖時,state加RUINT(值爲1);當釋放讀鎖時,state減去RUINT。
  • bit7爲寫鎖標識,其值爲1表示已獲取寫鎖,值爲0表示已獲取讀鎖。當線程獲取寫鎖或釋放寫鎖時,都會將state加WBIT;
  • bit8—bit64:表示寫鎖版本,不論獲取寫鎖還是釋放寫鎖,其值都會改變。
    初始狀態bit8爲1,其他位爲0。

state常用狀態標識:

  • RUNIT:
    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001
  • WBIT:
    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1000 0000
  • RBITS:
    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0111 1111
  • RFULL:
    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0111 1110
  • ABITS:
    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1111 1111
  • SBITS:
    1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1000 0000
  • ORIGIN:
    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000

state常用狀態判斷:

  • 有無線程獲取寫狀態:state < WBIT,true:無,false:有;
  • 讀狀態是否溢出:(state & ABITS) < RFULL,true;否;false:是;
  • 獲取讀狀態: state + RUNIT(或者readerOverflow + 1)
  • 獲取寫狀態: state + WBIT
  • 釋放讀狀態: state - RUNIT(或者readerOverflow - 1)
  • 釋放寫狀態: (s += WBIT) == 0L ? ORIGIN : s
  • 是否爲寫鎖: (state & WBIT) != 0L
  • 是否爲讀鎖: (state & RBITS) != 0L

2.4、寫鎖獲取及釋放

獲取寫鎖:

public long writeLock() {
    long s, next;  
    //(state & ABITS)獲取低8位,判斷有無讀/寫鎖存在,
    //有其他讀鎖則bit0-bit6不爲0,有其他寫鎖則bit7不爲0,
    //因此如果有別的寫鎖或者讀鎖存在將失敗
    //嘗試CAS獲取寫鎖
    //失敗調用acquireWrite繼續獲取寫鎖
    return ((((s = state) & ABITS) == 0L &&
             U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
            next : acquireWrite(false, 0L));
}
private long acquireWrite(boolean interruptible, long deadline) {
    //node爲當前節點,p爲當前節點的前驅節點
    WNode node = null, p;
    
    // 第一次自旋——主要進行入隊工作
    for (int spins = -1;;) { // spin while enqueuing
        long m, s, ns;
        //(state&ABITS)爲0表示無讀/寫鎖,則嘗試CAS獲取寫鎖
        if ((m = (s = state) & ABITS) == 0L) {
            if (U.compareAndSwapLong(this, STATE, s, ns = s + WBIT))
                return ns;
        }
        else if (spins < 0)
            // 如果自旋次數小於0,則計算自旋的次數
            // 如果當前有寫鎖(m == WBIT)獨佔,且隊列無元素(wtail == whead),
            // 說明當前節點的前驅節點爲獲取獨佔鎖的節點,鎖很快就會釋放,
            // 就自旋SPINS次就行了,如果自旋完了還沒輪到自己才入隊
            // 否則自旋次數爲0
            spins = (m == WBIT && wtail == whead) ? SPINS : 0;
        else if (spins > 0) {
            // 當自旋次數大於0時,當前這次自旋隨機減一次自旋次數
            if (LockSupport.nextSecondarySeed() >= 0)
                --spins;
        }
        else if ((p = wtail) == null) { // initialize queue
            // 如果隊列未初始化,新建一個空節點並初始化頭節點和尾節點
            WNode hd = new WNode(WMODE, null);
            if (U.compareAndSwapObject(this, WHEAD, null, hd))
                wtail = hd;
        }
        else if (node == null)
            // 如果新增節點還未初始化,則新建之,並賦值其前置節點爲尾節點
            node = new WNode(WMODE, p);
        else if (node.prev != p)
            // 如果新增節點的前驅節點不是尾節點,
            // 則更新新增節點的前驅節點爲新的尾節點
            node.prev = p;
        else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
            // 嘗試更新新增節點爲新的尾節點成功,則退出循環
            p.next = node;
            break;
        }
    }

    // 第二次自旋——主要進行阻塞並等待喚醒
    for (int spins = -1;;) {
        // h爲頭節點,np爲新增節點的前置節點,pp爲前前置節點,ps爲前置節點的狀態
        WNode h, np, pp; int ps;
        // 如果頭節點等於前置節點,說明快輪到自己了
        if ((h = whead) == p) {
            if (spins < 0)
                // 自旋次數小於0,則初始化自旋次數
                spins = HEAD_SPINS;
            else if (spins < MAX_HEAD_SPINS)
                // 自旋次數小於頭結點最大自旋次數,則增加自旋次數
                spins <<= 1;
                
            // 第三次自旋,不斷嘗試獲取寫鎖    
            for (int k = spins;;) { // spin at head
                long s, ns;
                //無讀寫鎖則CAS獲取寫鎖,成功則更新節點信息
                if (((s = state) & ABITS) == 0L) {
                    if (U.compareAndSwapLong(this, STATE, s,
                                             ns = s + WBIT)) {
                        whead = node;
                        node.prev = null;
                        return ns;
                    }
                }
                // 隨機立減自旋次數,當自旋次數減爲0時跳出循環再重試
                else if (LockSupport.nextSecondarySeed() >= 0 &&
                         --k <= 0)
                    break;
            }
        }
        //頭節點爲空?
        else if (h != null) { // help release stale waiters
            WNode c; Thread w;
            // 如果頭節點的cowait鏈表(棧)不爲空,喚醒裏面的所有節點
            while ((c = h.cowait) != null) {
                if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
                    (w = c.thread) != null)
                    U.unpark(w);
            }
        }
        
        // 如果頭節點沒有變化
        if (whead == h) {
            // 如果尾節點有變化,則更新
            if ((np = node.prev) != p) {
                if (np != null)
                    (p = np).next = node;   // stale
            }
            else if ((ps = p.status) == 0)
                // 如果尾節點狀態爲0,則更新成WAITING
                U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
            else if (ps == CANCELLED) {
                // 如果尾節點狀態爲取消,則把它從鏈表中刪除
                if ((pp = p.prev) != null) {
                    node.prev = pp;
                    pp.next = node;
                }
            }
            else {
                // 有超時時間的處理
                long time; // 0 argument to park means no timeout
                if (deadline == 0L)
                    time = 0L;
                //時間以過期則取消節點    
                else if ((time = deadline - System.nanoTime()) <= 0L)
                    return cancelWaiter(node, node, false);
                    
                //設置線程blocker    
                Thread wt = Thread.currentThread();
                U.putObject(wt, PARKBLOCKER, this);
                node.thread = wt;
                //1、當前節點前驅節點狀態爲WAITING;
                //2、當前節點的前驅節點不是頭節點或有讀寫鎖已經被獲取;
                //3、頭結點爲改變;
                //4、當前節點的前驅節點未改變;
                //當以上4個條件都滿足時將當前節點進行阻塞
                if (p.status < 0 && (p != h || (state & ABITS) != 0L) &&
                    whead == h && node.prev == p)
                    U.park(false, time);  // emulate LockSupport.park
                node.thread = null;
                //設置線程blocker爲空
                U.putObject(wt, PARKBLOCKER, null);
                //當前節點百中斷則取消節點
                if (interruptible && Thread.interrupted())
                    return cancelWaiter(node, node, true);
            }
        }
    }
}

寫鎖釋放:

public void unlockWrite(long stamp) {
    WNode h;
    //因爲寫鎖是獨佔鎖,可以簡單判斷state != stamp;
    //或者bit7爲0,即stamp狀態爲無寫鎖
    if (state != stamp || (stamp & WBIT) == 0L)
        throw new IllegalMonitorStateException();
    
    //修改state狀態,state += WBIT;溢出則初始化爲ORIGIN        
    state = (stamp += WBIT) == 0L ? ORIGIN : stamp;
    //頭節點不爲空,且狀態正常則釋放頭結點的鎖
    if ((h = whead) != null && h.status != 0)
        release(h);
}
private void release(WNode h) {
    if (h != null) {
        WNode q; Thread w;
        //設置頭節點狀態爲0
        U.compareAndSwapInt(h, WSTATUS, WAITING, 0);
        
        //頭節點下個節點爲空或狀態爲CANCEL,則從尾節點前向遍歷,
        //找到頭結點後面的第一個有效節點(狀態爲0或WAITTING)
        if ((q = h.next) == null || q.status == CANCELLED) {
            for (WNode t = wtail; t != null && t != h; t = t.prev)
                if (t.status <= 0)
                    q = t;
        }
        //喚醒下一個有效節點
        if (q != null && (w = q.thread) != null)
            U.unpark(w);
    }
}

2.5、讀鎖獲取及釋放

讀鎖獲取:

public long readLock() {
    long s = state, next;  // bypass acquireRead on common uncontended case
    //同步隊列爲空,讀鎖計數值未超過最大值(無寫鎖),CAS獲取鎖成功,則直接返回郵戳值;
    //否則acquireRead獲取讀鎖
    return ((whead == wtail && (s & ABITS) < RFULL &&
             U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
            next : acquireRead(false, 0L));
}
private long acquireRead(boolean interruptible, long deadline) {
    WNode node = null, p;
    for (int spins = -1;;) {
        WNode h;
        //如果同步隊列頭節點等於尾節點,說明隊列中沒有節點或者只有一個節點
        //則自旋一段時間,等待頭結點釋放鎖後獲取鎖
        if ((h = whead) == (p = wtail)) {
            for (long m, s, ns;;) {
                //(state & ABITS)小於RFULL,表示無寫鎖,則直接CAS獲取讀鎖;
                //否則若(state & ABITS)小於WBIT表示無寫鎖但讀鎖計數已溢出,
                //則CAS更新讀鎖計數獲取讀鎖
                if ((m = (s = state) & ABITS) < RFULL ?
                    U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
                    (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L))
                    return ns;
                //(state & ABITS)大於WBIT,表示寫鎖已經被佔,則自旋    
                else if (m >= WBIT) {
                    //隨機減自旋次數
                    if (spins > 0) {
                        if (LockSupport.nextSecondarySeed() >= 0)
                            --spins;
                    }
                    else {
                        //自旋完還沒獲取到讀鎖?
                        if (spins == 0) {
                            WNode nh = whead, np = wtail;
                            //判斷穩定性(有沒有被修改),跳出循環
                            if ((nh == h && np == p) || (h = nh) != (p = np))
                                break;
                        }
                        //初始化spins
                        spins = SPINS;
                    }
                }
            }
        }
        //自旋獲取失敗,則進行節點初始化相關的處理
        //尾節點爲空,則初始化隊列
        if (p == null) { // initialize queue
            WNode hd = new WNode(WMODE, null);
            if (U.compareAndSwapObject(this, WHEAD, null, hd))
                wtail = hd;
        }
        //初始化代表當前讀線程的節點
        else if (node == null)
            node = new WNode(RMODE, p);
        //head==tail或者隊列tail.mode不爲讀狀態,
        //那麼將當前線程的節點node加入到隊列尾部並跳出外層循環    
        else if (h == p || p.mode != RMODE) {
            if (node.prev != p)
                node.prev = p;
            else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
                p.next = node;
                break;
            }
        }
        //如果head!= tail說明隊列中已經有線程在等待或者tail.mode是讀狀態RMODE,
        //那麼CAS方式將當前線程的節點node加入到tail節點的cowait鏈中
        else if (!U.compareAndSwapObject(p, WCOWAIT,
                                         node.cowait = p.cowait, node))
            node.cowait = null;
        else {
            for (;;) {
                WNode pp, c; Thread w;
                ////如果head不爲空那麼嘗試去解放head的cowait鏈中的節點
                if ((h = whead) != null && (c = h.cowait) != null &&
                    U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
                    (w = c.thread) != null) // help release
                    U.unpark(w);
                //如果tail節點的前驅就是head或者head==tail或者tail節點的前驅是null
                //也就是說當前node所在的節點(因爲node可能在cowait鏈中)
                //的前驅就是head或者head已經被釋放了爲null    
                if (h == (pp = p.prev) || h == p || pp == null) {
                    long m, s, ns;
                    do {
                        //如果沒有寫狀態被佔有那麼自旋方式嘗試獲取讀狀態,成功則返回stamp
                        if ((m = (s = state) & ABITS) < RFULL ?
                            U.compareAndSwapLong(this, STATE, s,
                                                 ns = s + RUNIT) :
                            (m < WBIT &&
                             (ns = tryIncReaderOverflow(s)) != 0L))
                            return ns;
                    } while (m < WBIT);
                }
                //判斷是否穩定
                if (whead == h && p.prev == pp) {
                    long time;
                    //如果tail的前驅是null或者head==tail或者tail已經被取消了(p.status > 0)
                    //直接將node置爲null跳出循環,回到最開的for循環中去再次嘗試獲取同步狀態
                    if (pp == null || h == p || p.status > 0) {
                        node = null; // throw away
                        break;
                    }
                    if (deadline == 0L)
                        time = 0L;
                    //如果超時則取消當前線程    
                    else if ((time = deadline - System.nanoTime()) <= 0L)
                        return cancelWaiter(node, p, false);
                    Thread wt = Thread.currentThread();
                    U.putObject(wt, PARKBLOCKER, this);
                    node.thread = wt;
                    
                    //tail的前驅不是head或者當前只有寫線程獲取到同步狀態
                    //判斷穩定性
                    if ((h != pp || (state & ABITS) == WBIT) &&
                        whead == h && p.prev == pp)
                        U.park(false, time);
                    node.thread = null;
                    U.putObject(wt, PARKBLOCKER, null);
                    //中斷的話取消
                    if (interruptible && Thread.interrupted())
                        return cancelWaiter(node, p, true);
                }
            }
        }
    }

     //如果隊列中沒有節點或者tail的mode是WMODE寫狀態,
     //那麼node被加入到隊列的tail之後進入這個循環
    for (int spins = -1;;) {
        WNode h, np, pp; int ps;
        //如果p(node的前驅節點)就是head,那麼自旋方式嘗試獲取同步狀態
        if ((h = whead) == p) {
            //第一次循環,設置自旋次數
            if (spins < 0)
                spins = HEAD_SPINS;
            //自旋次數增加    
            else if (spins < MAX_HEAD_SPINS)
                spins <<= 1;
            for (int k = spins;;) { // spin at head
                long m, s, ns;
                //自旋方式嘗試獲取同步狀態
                //獲取成功的話將node設置爲head並解放node的cowait鏈中的節點並返回stamp
                if ((m = (s = state) & ABITS) < RFULL ?
                    U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
                    (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
                    WNode c; Thread w;
                    whead = node;
                    node.prev = null;
                    while ((c = node.cowait) != null) {
                        if (U.compareAndSwapObject(node, WCOWAIT,
                                                   c, c.cowait) &&
                            (w = c.thread) != null)
                            U.unpark(w);
                    }
                    return ns;
                }
                //如果有寫線程獲取到了同步狀態(因爲可能有寫線程闖入)那麼隨機的--k控制循環次數
                else if (m >= WBIT &&
                         LockSupport.nextSecondarySeed() >= 0 && --k <= 0)
                    break;
            }
        }
        //如果head不爲null,解放head的cowait鏈中的節點
        else if (h != null) {
            WNode c; Thread w;
            while ((c = h.cowait) != null) {
                if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
                    (w = c.thread) != null)
                    U.unpark(w);
            }
        }
         //判斷穩定性
        if (whead == h) {
            if ((np = node.prev) != p) {
                if (np != null)
                    (p = np).next = node;   // stale
            }
            //嘗試設tail的狀態位WAITING表示後面還有等待的節點
            else if ((ps = p.status) == 0)
                U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
            //如果tail已經取消了    
            else if (ps == CANCELLED) {
                if ((pp = p.prev) != null) {
                    node.prev = pp;
                    pp.next = node;
                }
            }
            else {
                //超時判定
                long time;
                if (deadline == 0L)
                    time = 0L;
                else if ((time = deadline - System.nanoTime()) <= 0L)
                    return cancelWaiter(node, node, false);
                Thread wt = Thread.currentThread();
                U.putObject(wt, PARKBLOCKER, this);
                node.thread = wt;
                //阻塞等待
                if (p.status < 0 &&
                    (p != h || (state & ABITS) == WBIT) &&
                    whead == h && node.prev == p)
                    U.park(false, time);
                node.thread = null;
                U.putObject(wt, PARKBLOCKER, null);
                //中斷處理
                if (interruptible && Thread.interrupted())
                    return cancelWaiter(node, node, true);
            }
        }
    }
}

讀鎖釋放:

public void unlockRead(long stamp) {
    long s, m; WNode h;
    for (;;) {
        //寫計數相關的bit位有改變,或讀計數相關的bit位爲0,或有寫鎖,則異常
        if (((s = state) & SBITS) != (stamp & SBITS) ||
            (stamp & ABITS) == 0L || (m = s & ABITS) == 0L || m == WBIT)
            throw new IllegalMonitorStateException();
        //讀鎖計數未溢出?    
        if (m < RFULL) {
            if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
                if (m == RUNIT && (h = whead) != null && h.status != 0)
                    release(h);
                break;
            }
        }
       //讀鎖計數溢出?    
        else if (tryDecReaderOverflow(s) != 0L)
            break;
    }
}

2.6、樂觀鎖獲取

public long tryOptimisticRead() {
    long s;
    //當無寫鎖,則返回(state & SBITS),即高位的寫計數;
    //否則返回0,即獲取樂觀鎖失敗
    return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L;
}

2.7、鎖模式的轉換

轉換爲寫鎖:將其他模式(如寫鎖、讀鎖、樂觀鎖)的鎖轉換爲寫鎖

public long tryConvertToWriteLock(long stamp) {
    long a = stamp & ABITS, m, s, next;
    //寫鎖的狀態未變?
    while (((s = state) & SBITS) == (stamp & SBITS)) {
        //當前無寫/讀鎖?CAS獲取寫鎖
        if ((m = s & ABITS) == 0L) {
            if (a != 0L)
                break;
            if (U.compareAndSwapLong(this, STATE, s, next = s + WBIT))
                return next;
        }
        //當前有寫鎖,且狀態不變,則直接返回原本的版本郵戳
        else if (m == WBIT) {
            //寫線程不爲當前線程?錯誤,跳出循環
            if (a != m)
                break;
            return stamp;
        }
        //無寫鎖,有一個讀鎖,即當前線程爲獲取讀鎖的線程
        //直接cas將讀鎖釋放並獲取寫鎖,即state=(state - RUNIT + WBIT)
        else if (m == RUNIT && a != 0L) {
            if (U.compareAndSwapLong(this, STATE, s,
                                     next = s - RUNIT + WBIT))
                return next;
        }
        //當還有其他線程獲取讀鎖時,自旋等待其他讀鎖釋放
        else
            break;
    }
    return 0L;
}

主要處理:
當鎖狀態爲無讀/寫鎖時,通過CAS獲取寫鎖;
噹噹前線程已獲取寫鎖時,直接返回原本的郵戳;
當只有一個讀鎖,即當只有當前線程獲取讀鎖,沒有其他的讀鎖時,CAS釋放讀鎖並獲取寫鎖;
當獲取讀鎖的線程數大於1,即除當前線程還有其他線程也獲取到讀鎖時,自旋等待其他讀鎖釋放;

轉換爲讀鎖:將讀/寫鎖轉換爲讀鎖;

public long tryConvertToReadLock(long stamp) {
    long a = stamp & ABITS, m, s, next; WNode h;
    //無寫鎖?
    while (((s = state) & SBITS) == (stamp & SBITS)) {
        
        //無讀/寫鎖,則CAS獲取讀鎖
        if ((m = s & ABITS) == 0L) {
            //郵戳錯誤?退出循環
            if (a != 0L)
                break;
            //讀鎖計數未溢出?    
            else if (m < RFULL) {
                if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
                    return next;
            }
            //讀鎖計數溢出?
            else if ((next = tryIncReaderOverflow(s)) != 0L)
                return next;
        }
        //有寫鎖,且寫鎖爲當前線程?則釋放讀鎖並喚醒後續寫鎖線程
        else if (m == WBIT) {
            //寫鎖不爲當前線程,錯誤,跳出循環
            if (a != m)
                break;
            state = next = s + (WBIT + RUNIT);
            if ((h = whead) != null && h.status != 0)
                release(h);
            return next;
        }
        //當前線程已經獲取得讀鎖?
        else if (a != 0L && a < WBIT)
            return stamp;
        else
            break;
    }
    return 0L;
}

主要處理:
若無讀/寫鎖,則CAS獲取讀鎖;
若寫鎖爲當前線程,則CAS釋放寫鎖並獲取讀鎖;
若當前線程已經獲取讀鎖,則返回原本的郵戳;

轉換爲樂觀鎖:

public long tryConvertToOptimisticRead(long stamp) {
    long a = stamp & ABITS, m, s, next; WNode h;
    U.loadFence();
    for (;;) {
        //寫鎖狀態變更?
        if (((s = state) & SBITS) != (stamp & SBITS))
            break;
        //無讀/寫鎖?直接返回state    
        if ((m = s & ABITS) == 0L) {
            if (a != 0L)
                break;
            return s;
        }
        //已經獲取寫鎖?則直接釋放寫鎖
        else if (m == WBIT) {
            if (a != m)
                break;
            state = next = (s += WBIT) == 0L ? ORIGIN : s;
            if ((h = whead) != null && h.status != 0)
                release(h);
            return next;
        }
        //狀態爲無讀鎖或無寫鎖?
        else if (a == 0L || a >= WBIT)
            break;
        //讀鎖計數未溢出    
        else if (m < RFULL) {
            if (U.compareAndSwapLong(this, STATE, s, next = s - RUNIT)) {
                if (m == RUNIT && (h = whead) != null && h.status != 0)
                    release(h);
                return next & SBITS;
            }
        }
        //讀鎖計數溢出?
        else if ((next = tryDecReaderOverflow(s)) != 0L)
            return next & SBITS;
    }
    return 0L;
}

主要處理:
若當前線程已經獲取寫鎖,則直接釋放寫鎖;
若當前線程已經獲取讀鎖,則CAS釋放讀鎖;
當鎖時,返回狀態值state;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章