lock源碼分析

1、concurrent包層次結構

      包內有atomic和lock兩個子包,還有阻塞隊列以及executors,併發容器,同步工具;這些的實現都依賴於volatile變量讀寫和CAS

 

2、lock簡介

      含義:java5之後增加了lock接口,提供和synchronized一樣的鎖功能;其操作靈活性更強,鎖的種類更豐富支持非公平鎖和可重入鎖且可以自定義去實現不同功能需求的鎖-公平,非公平,獨佔,共享; 可獲取鎖的狀態-解決死鎖問題;

      API

void lock(); //獲取鎖

void unlock(); //釋放鎖
void lockInterruptibly() throws InterruptedException;//獲取鎖的過程能夠響應中斷
boolean tryLock();//非阻塞式響應中斷能立即返回,獲取鎖放回true反之返回fasle
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;//超時獲取鎖,在超時內或者未中斷的情況下能夠獲取鎖
Condition newCondition();//獲取與lock綁定的等待通知組件,當前線程必須獲得了鎖才能進行等待,進行等待時會先釋放鎖,當再次獲取鎖時才能從等待中返回

     Lock的除Condition以外的demo

package com.hezm.thread.day1.theory;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {

    //可重入鎖(當前線程調用lock.lock()方法可以再次lock.lock進入,不會阻塞)  
    //  解決:死鎖問題;
    //synchronized也是支持可重入的;
    static Lock lock = new ReentrantLock();
    static Lock lock1 = new ReentrantLock(true);  //公平鎖


    //可重入鎖特性;  
    public static void main(String[] args) {
        //場景1:獲取鎖,成功則進入,不成功則阻塞等待一個一個執行
        lock.lock();   //獲得鎖
        demo1()
        lock.unlock();   //釋放鎖
    }


    /**synchronized 重入互斥鎖 案例*/
    public synchronized void demo1() {
        System.out.println("我鎖住了this,我想再次進入獲取鎖");
        demo2();
    }
    public synchronized void demo2() {
        synchronized (this){
            System.out.println("我再次進來了,增加重入次數1");
            //todo
        }
        System.out.println("我出來第一把鎖了,減少重入次數1");
    }


    //場景2:如果已經被lock則立即返回false,忽略操作,不會等待。
    public synchronized void demo3() {
        if(lock.tryLock()){
            try{
            }finally{
                lock.unlock
            }
        }
    }


    //場景3:如果發現該操作已經在執行,則等待一段時間,超時則不執行
    public synchronized void demo3() {
        if(lock.tryLock(5,TimeUnit.SECONDS)){
            try{
            }finally{
                lock.unlock
            }
        }
    }


    //場景4:如果發現該操作已經在執行,等待執行,可中斷正在執行的操作立刻釋放鎖,繼續下一操作
    public synchronized void demo3() {
        try{
            lock.lockInterruptibly();
        }finally{
            lock.unlock
        }
        
    }
}

        Contion我們有專門的分析文章,詳情請看線程控制工具案例詳解

        要想弄清楚lock的底層原理,和其他類相比我們還不能馬上進入看源碼的過程,我們還需要先了解其構建的基礎:AQS(AbstractQueuedSynchronizer);

 

3、AQS詳解:

        含義:

            抽象隊列同步器,是其他同步組件的基礎框架,依賴一個int成員變量來表示同步狀態以及通過FIFO隊列構成等待隊列; 

            AQS採用模板方法的設計模式,在同步組件的實現中推薦定義繼承AQS的靜態內存類;

            子類重寫AQS的tryAcquire-獨佔式獲取同步狀態、tryRelease-獨佔式釋放同步狀態、tryAcquireShared-共享式獲取同步狀態、tryReleaseShared-共享式釋放同步狀態、isHeldExclusively-是否被當前線程獨佔;  方法實現對鎖的自定義實現

            狀態的更新使用getState,setState以及compareAndSetState這三個方法

            AQS封裝了同步狀態的管理,線程排隊,等待和喚醒等操作;

      核心API:

            AQS提供的模板方法: 分爲:

                  獨佔式獲取和釋放同步狀態 : acquire() ,  acquireInterruptibly(),  tryAcquireNanos(), release()

                  共享式獲取和釋放同步狀態: acquireShared(), acquireSharedInterruptibly(), tryAcquireSharedNanos(), releaseShared();

                  查詢同步隊列中等待線程情況:getQueuedThreads()

             數據結構:

                  head指向頭結點,tail指向尾節點的單向鏈表結構;head始終保持線程值爲null,正在被執行的線程保存在AbstractOwnableSynchronizer ;
                     *      +------+  prev +-----+       +-----+
                     * head | node | <----| node | <---- | node |  tail
                     *      +------+       +-----+       +-----+

                 waitStatus常量值含義:

                      CANCELLED  1    同步隊列中等待的線程超時或被中斷,需要從同步隊列中取消該Node的節點,節點終極狀態

                      SIGNAL     -1  等待喚醒狀態的後繼結點,當其前繼結點的線程釋放了同步鎖或被取消,將會通知後繼節點的線程執行;

                      CONDITION   -2  標識節點處於等待隊列中,當其他線程調用Condition.signal()該狀態的節點從等待隊列轉移到同步隊列中,等待獲取同步鎖;

                      PROPAGATE  -3   共享模式中標識結點的線程處於可運行狀態

                     0                      0     初始狀態 

 

            AQS核心模板API源碼分析

                 獨佔鎖獲取方法acquire():

//獨佔鎖獲取
public final void acquire(int arg) {
        //獲取同步狀態成功,則方法結束返回
        //若失敗則先調用addWaiter()方法再調用acquireQueued()方法實現入隊列操作;
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
}



private Node addWaiter(Node mode) {
        // 1. 將當前線程構建成Node類型
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        // 2. 當前尾節點是否爲null?
        Node pred = tail;
        if (pred != null) {
            // 2.2 將當前節點尾插入的方式插入同步隊列中
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {  //CAS操作自旋重試
                pred.next = node;
                return node;
            }
        }
        // 2.1. 當前同步隊列尾節點爲null,說明當前線程是第一個加入同步隊列進行等待的線程
        enq(node);
        return node;
}



private Node enq(final Node node) {
        for (;;) {     //自旋插入隊列
            Node t = tail;
            if (t == null) { // 第一個進入的時候首先構造頭結點;
                //1. 構造頭結點
                if (compareAndSetHead(new Node()))    //cas操作設置頭節點
                    tail = head;
            } else {
                // 2. 尾插入,CAS操作失敗自旋嘗試
                node.prev = t;
                if (compareAndSetTail(t, node)) {   //cas操作設置尾結點
                    t.next = node;
                    return t;
                }
            }
        }
}


//獲取鎖成功/失敗的處理
final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {   //自旋
                // 1. 獲得當前節點的先驅節點
                final Node p = node.predecessor();
                // 2. 當前節點能否獲取獨佔式鎖                  
                // 2.1 如果當前節點的先驅節點是頭結點並且成功獲取同步狀態,即可以獲得獨佔式鎖
                if (p == head && tryAcquire(arg)) {   //獲取鎖成功的出隊操作;
                    //隊列頭節點指向當前節點,當前節點的prev設置爲null,
                    setHead(node);
                    //幫助GC前驅節點
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // 2.2 獲取鎖失敗,線程進入等待狀態等待獲取獨佔式鎖
                if (shouldParkAfterFailedAcquire(p, node) &&   //cas將節點狀態設置成SIGNAL,表示線程阻塞;失敗則返回false,自旋調用acquireQueued()重試;
                    parkAndCheckInterrupt())   //阻塞線程
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
}

            獨佔鎖的釋放 release()喚醒後繼節點:

public final boolean release(int arg) {
        if (tryRelease(arg)) {  //釋放成功;
            Node h = head;
            if (h != null && h.waitStatus != 0)  //節點存在且狀態值不爲0執行unpark()喚醒
                unparkSuccessor(h);
            return true;
        }
        return false;
}



private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    //頭節點的後繼節點
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        //爲何從尾節點遍歷?
        //  {enq()中1,prev賦值 2,tail指向 3,next賦值}時候不是原子操作,從頭節點遍歷會造成鏈表斷裂;
        for (Node t = tail; t != null && t != node; t = t.prev) //
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        //後繼節點不爲null時喚醒該線程
        LockSupport.unpark(s.thread);
}

         可中斷式獲取鎖acquireInterruptibly():

public final void acquireInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (!tryAcquire(arg))
        //線程獲取鎖失敗
        doAcquireInterruptibly(arg);
}



private void doAcquireInterruptibly(int arg)
    throws InterruptedException {
    //將節點插入到同步隊列中
    final Node node = addWaiter(Node.EXCLUSIVE);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            //獲取鎖出隊
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())  
                //線程中斷拋異常
                throw new InterruptedException();
        }
    } finally {
        if (failed)   
            cancelAcquire(node);
    }
}

 超時等待式獲取鎖 tryAcquireNanos()

        

 

              共享鎖的工作邏輯:

                     1、當線程調用acquireShared()申請獲取鎖資源,成功則進入臨界區

                     2、當獲取鎖失敗時,則創建一個共享類型的節點並進入一個FIFO等待隊列,被掛起等待喚醒

                     3、當隊列中等待線程被喚醒後重新嘗試獲取鎖資源,成功則喚醒後面還在等待的共享節點並把該喚醒事件傳遞下去,即依次喚醒在該節點後面的所有共享節點,然後進入臨界區,否則繼續掛起等待;setHeadAndPropagate()  方法實現

 

              共享鎖獲取acquireShared()  :

public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)   //  大於等於0表示獲取鎖成功
        doAcquireShared(arg);    //鎖獲取失敗加入隊列
}


private void doAcquireShared(int arg) {
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    // 當該節點的前驅節點是頭結點且成功獲取同步狀態
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    if (interrupted)
                        selfInterrupt();
                    failed = false;
                    return;    //自旋退出條件:當前節點的前驅節點是頭節點且獲取鎖成功
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}


    private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // 記錄下舊頭節點以便檢查
        setHead(node);  //把當前獲取的鎖節點設置爲頭節點
        //執行喚醒操作的兩種情況:①調用方指明後繼節點需要被喚醒;   
        //                        ②頭節點的後面節點需要被喚醒
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared()) //如果當前節點的後繼節點是共享類型,則喚醒後繼節點;
                doReleaseShared();//喚醒後繼節點
        }
    }



   //喚醒後繼節點
   private void doReleaseShared() {
         for (;;) {   //自旋
            Node h = head;  //喚醒由新的頭節點開始
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {  //表示該後繼節點需要被喚醒;
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))  //控制併發setHeadAndPropagate 和 release()兩個入口;
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);   //執行喚醒操作
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // 如果後繼節點不需要喚醒則把當前節點狀態設置爲PROPAGATE,確保以後可以傳遞下去;
            }
            if (h == head)   // 如果頭節點發生變化,說明其他線程獲取到了鎖,進行重試,以使得喚醒動作可以傳遞;
                break;
        }
    }


                 共享鎖釋放releaseShared():

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {   //成功釋放同步狀態後
        doReleaseShared();  
        return true;
    }
    return false;
}


private void doReleaseShared() {
    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);   //  喚醒成功,之前的頭結點阻塞進入setHeadAndPropagate(node, r)處理邏輯;
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))  //沒有後繼者設置狀態
                continue;                // loop on failed CAS
        }
        if (h == head)                   // 如果頭節點發生變化進行重試
            break;
    }
}

 

其他關鍵點

      synchronized 與lock的區別?

     *    ① synchronized是一個關鍵詞,lock是一個接口 {基本實現:tryLock, unLock};
     *    ② synchronized:是被動釋放鎖,同步代碼塊執行完或異常的時候釋放。
     *       lock可以主動選擇什麼時候獲得鎖,釋放鎖;
     *    ④ lock有公平鎖和非公平鎖  ;  synchronized只有非公平鎖
     *    ④ lock可以判斷鎖的狀態,可解決死鎖問題tryLock(),  synchronized無法判斷。

     獨佔鎖和共享鎖的區別?

獨佔(互斥-不可共用)

共享(共享-可以共享);

 

可重入鎖的源碼分析:

 //可重入鎖特性;  
public static void main(String[] args) {
    //場景1:獲取鎖,成功則進入,不成功則阻塞等待一個一個執行
    lock.lock();   //獲得鎖
    demo1()
    lock.unlock();   //釋放鎖
}


//以下分析走非公平鎖邏輯:

//lock()
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            //獨佔式獲取鎖,重寫了tryAcquire()邏輯;
            //   當線程狀態 == 0 則搶鎖,搶成功則執行(插隊);
            //   不爲0爲當前正在執行線程重入的則狀態值計數 +1;(可重入鎖的邏輯)
            //       以上都不是則返回false繼續執行 
            acquire(1);             
    }



//unlock()
   public final boolean release(int arg) {
        if (tryRelease(arg)) {    //重寫了釋放邏輯
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }


        protected final boolean tryRelease(int releases) {  //可重入鎖
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {   //歸置爲0,釋放鎖成功
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }


    /***
     *  學習筆記:
     *
     * Lock的實現原理(如何實現多個線程在競爭的時候的阻塞和同步):
     *      實現有:讀寫鎖、  重入鎖 ReentrantLock (唯一實現){
     *          核心實現: 抽象隊列同步器 AbstractQueuedSynchronizer  -->  實現 Sync   包含  FairSync  與 NofairSync 兩種具體實現{
     *              核心功能:獨佔(互斥-不可共用)、共享(共享-可以共享);
     *
     *              lock.lock()核心調用方法:
     *                  NofairSync # compareAndSetState(0, 1)
     *                  1,當保存的值爲0的時候則鎖搶佔成功,爭搶鎖成功,設置內存偏移量爲1,則執行當前線程(setExclusiveOwnerThread(Thread.currentThread()););
     *                      Unsafe # compareAndSwapInt(Object 鎖對象, long 內存地址偏移量, int 預期值, int 修改值):
     *                          【解釋】:當預期值和偏移量中值相等則修改值返回true,否則不修改返回false;
     *                 2,當鎖已經被搶佔,後面進來的線程則調用acquire(1);
     *                      !tryAcquire(arg){
     *                           NofairSync # nonfairTryAcquire(acquires)  返回true/false{
     *                                當線程狀態 == 0 則搶鎖,搶成功則執行(插隊);
     *                                不爲0爲當前正在執行線程重入的則狀態值計數 +1;
     *                                以上都不是則返回false繼續執行 acquireQueued(addWaiter(Node.EXCLUSIVE), arg);
     *                            }
     *                      }
     *
     *                      AbstractQueuedSynchronizer # addWaiter(Node.EXCLUSIVE), arg{
     *                          把當前線程封裝爲獨佔鎖(Node.EXCLUSIVE)Node{
     *                              1、tail不爲null(隊列不存在其他線程){
     *                                     傳入的node{thread = threadC;waitState = 0 }節點pre設置爲node{thread = threadB;waitState = 0 };
     *                                     把AQS實現將tail指向  node{thread = threadC;waitState = 0 }
     *                                     node{thread = threadB;waitState = 0 }中的next指向node{thread = threadC;waitState = 0 }
     *                              }
     *                              2、tail爲null則調用  enq(node)  自旋鎖實現(首次進來自旋兩次){
     *                                  1,Cas初始化一個空的Node節點( thread = null;waitState = 0 ),head 和 tail節點都指向他;
     *                                  2,  傳入的node{thread = threadB;waitState = 0 }節點pre設置爲 前面初始話的Node節點;
     *                                      把AQS實現將tail指向  node{thread = threadB;waitState = 0 }
     *                                      初始化的空Node中的next指向node{thread = threadB;waitState = 0 }
     *                              }
     *                          }
     *                      }
     *                       AbstractQueuedSynchronizer # acquireQueued(node){
     *                            final Node p = node.predecessor();  拿到線程B的前一個節點:頭節點
     *                            tryAcquire(arg) 爭搶鎖
     *                            shouldParkAfterFailedAcquire(p, node)  爭搶鎖失敗後是否 park{
     *                                1、查看p線程的狀態是否爲 SINGLA  該狀態的線程在鎖釋放的時候會被喚醒;  是則park
     *                                2、爲 CANAELLED 的時候,線程超時或被中斷的時候  把 CANAELLED 的節點丟掉;
     *                                3、標註當前節點的前一個節點爲 SINGLA {初始化的Node, node{thread = threadB;waitState = 0 } 均被改變}
     *                            }
     *                            parkAndCheckInterrupt(){
     *                                LockSupport.park(this);  掛起當前的線程;( node{thread = threadB;waitState = 0 }   node{thread = threadC;waitState = 0 } )被掛起
     *                                Thread.interrupted();  復位,返回是否被中斷過的標誌;【線程在park中無法響應線程的中斷信息】
     *                            }
     *                       }
     *                      selfInterrupt();該方法響應之前將中斷線程設置爲true的設定;中斷當前線程: Thread.currentThread().interrupt();
     *
     *
     *                 lock.unlock()核心調用方法:將狀態值修改、喚醒下一個線程{
     *                      tryRelease()   設置狀態值爲0,修改當前執行線程爲null;
     *                      unparkSuccessor(node){
     *                          更新初始化的Node 的 waitState 爲1;
     *                          下一個節點不爲null,則LockSupport.unpark( s.thread );   //喚醒線程B
     *                          下個節點爲null,(當節點狀態爲Cancelled)的時候從尾部進行遍歷,移除Cancelled的節點;{
     *                              這個設計原因:尾部遍歷避免尾部的鏈構建原子性問題{enq()中1,prev賦值 2,tail指向 3,next賦值}造成斷鏈;
     *                              原因:第3步next未賦值時中斷斷鏈,而prev第一步肯定已經完成;
     *                          }
     *                      }
     *                 }
     *                 喚醒之後線程在 AbstractQueuedSynchronizer # acquireQueued(node)中的線程被喚醒繼續執行;走之後的邏輯;{
     *                     1,head設置爲threadB,把原來Node(threadB)的線程設置爲null,pre設置爲null;
     *                     2,將之前Head的next設置爲null{刪除之前的head節點};  Node(threadB)變成了頭節點;
     *                 }
     *          }
     *      }
     *      【補充】:
     *   【中斷通信】
     *      Thread.interrupted();  //獲取中斷狀態並復位
     *      thread.interrupt();   //中斷線程
     *  【變量通信新機制】:
     *      LockSupport.unpark( s.thread )  喚醒線程
     *      LockSupport.park( s.thread )    掛起阻塞線程
     * 【公平鎖和非公平鎖的區別】
     *       1,鎖基本原理: 一次只能有一個線程獲取到鎖,而能獲取到鎖的標誌是AQS.STATE = 0  大於0表示已經有線程獲取到鎖了;獲取到鎖則狀態值+1;
     *       2,區別
     *                  lock()           tryAcquire()
     *       fair                       !hasQueuePredecessors如果隊列有
     *                                  阻塞則不進行搶佔
     *       noFair : lock直接插隊搶佔;
     *
     */

 

公平鎖 VS 非公平鎖:

  1. 公平鎖每次獲取到鎖爲同步隊列中的第一個節點,保證請求資源時間上的絕對順序,而非公平鎖有可能剛釋放鎖的線程下次繼續獲取該鎖,則有可能導致其他線程永遠無法獲取到鎖,造成“飢餓”現象

  2. 公平鎖爲了保證時間上的絕對順序,需要頻繁的上下文切換,而非公平鎖會降低一定的上下文切換,降低性能開銷。因此,ReentrantLock默認選擇的是非公平鎖,則是爲了減少一部分上下文切換,保證了系統更大的吞吐量

demo2:ReentrantReadWriteLock     

package com.hezm.thread.day1.theory;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**可重入讀寫鎖*/
public class ReentrantReadWriteLockDemo {
    static ReentrantReadWriteLock writeLock = new ReentrantReadWriteLock();    //可重入讀寫鎖;讀不需要加鎖。
    static Map<String, Object> cacheMap = new HashMap<>();

    static Lock read = writeLock.readLock();
    static Lock write = writeLock.writeLock();

    public static void main(String[] args) {
        /**
         * 適用於:讀多寫少的場景:
         * 讀->讀可以共享   // Thread2:在get的時候,  Thread1:線程在get的時候不會阻塞,可共享數據;
         * 讀->寫互斥   // Thread2:在put的時候,  Thread1:線程在get的時候會阻塞,等待寫完纔讀,讀到最新數據;
         * 寫->寫互斥  // Thread2:在put的時候,  Thread1:線程在put的時候會阻塞,等待寫完才put;
         */
        put("zhangsan","23歲");
        get("zhangsan");
    }

    //Thread1:讀鎖:讀的時候獲取最新數據
    public static final Object get(String key){
        System.out.println("begin read deta:" + key);
        read.lock();
        try {
            return cacheMap.get(key);
        } finally {
            read.unlock();   //防止死鎖,一直不釋放鎖
        }
    }

    //Thread2:寫鎖:寫操作對讀可見
    public static final Object put(String key, Object value){
        write.lock();
        try {
            System.out.println("begin write deta:" + key + "value" + value);
            return cacheMap.put(key,value);
        } finally {
            write.unlock();   //防止死鎖,一直不釋放鎖
        }
    }
}

  使用場景ReentrantLock是獨佔鎖, 在大部分爲讀數據,很少寫數據的場景下,這就是性能提升的關鍵點;而ReentrantReadWriteLock就爲這種場景提供瞭解決機制,在都是讀線程訪問的時候,可以多線程同時運行,在寫線程訪問的時候,所有的讀線程和寫線程會被阻塞;

         核心機制:

                    ①讀寫鎖如何分別記錄讀寫狀態

                    ②寫鎖如何獲取和釋放鎖

                    ③讀鎖怎樣獲取和釋放的?

                    ④鎖降級如何實現:寫鎖 降級 爲讀鎖;

源碼分析:

       ①讀寫鎖如何分別記錄讀寫狀態

        // 同步狀態的低16位表示寫鎖獲取次數;   c & EXCLUSIVE_MASK;
        // 同步狀態的高16位表示讀鎖被獲取的次數  c >>> SHARED_SHIFT;

       ②寫鎖如何獲取和釋放鎖

//寫鎖是獨佔鎖,通過重寫tryAcquire() 方法實現寫鎖的搶佔;
protected final boolean tryAcquire(int acquires) {
    Thread current = Thread.currentThread();
    // 1. 獲取寫鎖當前的同步狀態
    int c = getState();
    // 2. 獲取寫鎖獲取的次數
    int w = exclusiveCount(c);
    if (c != 0) {
        // (Note: if c != 0 and w == 0 then shared count != 0)
        // 3.1 當讀鎖已被讀線程獲取或者當前線程不是已經獲取寫鎖的線程的話
        // 當前線程獲取寫鎖失敗
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
        // 同步狀態的低16位表示寫鎖獲取次數;   c & EXCLUSIVE_MASK;
        // 同步狀態的高16位表示讀鎖被獲取的次數  c >>> SHARED_SHIFT;
        if (w + exclusiveCount(acquires) > MAX_COUNT)     
            throw new Error("Maximum lock count exceeded");
        // Reentrant acquire
        // 3.2 當前線程獲取寫鎖,支持可重複加鎖
        setState(c + acquires);
        return true;
    }
    // 3.3 寫鎖未被任何線程獲取,當前線程可獲取寫鎖
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false;
    setExclusiveOwnerThread(current);
    return true;
}



//寫鎖的釋放,重寫AQS的tryRelease方法實現
protected final boolean tryRelease(int releases) {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    //1. 同步狀態減去寫狀態
    int nextc = getState() - releases;
    //2. 當前寫狀態是否爲0,爲0則釋放寫鎖
    boolean free = exclusiveCount(nextc) == 0;
    if (free)
        setExclusiveOwnerThread(null);
    //3. 不爲0則更新同步狀態
    setState(nextc);
    return free;
}

       ③讀鎖怎樣獲取和釋放的?

//讀鎖的獲取
protected final int tryAcquireShared(int unused) {
    Thread current = Thread.currentThread();
    int c = getState();
    //1. 如果寫鎖已經被獲取並且獲取寫鎖的線程不是當前線程的話,當前
    // 線程獲取讀鎖失敗返回-1
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
    int r = sharedCount(c);   
    if (!readerShouldBlock() &&
        r < MAX_COUNT &&
        //2. 當前線程獲取讀鎖   SHARED_UNIT,高16位+1
        compareAndSetState(c, c + SHARED_UNIT)) {
        //3. 下面的代碼主要是新增的一些功能,比如getReadHoldCount()方法
        //返回當前獲取讀鎖的次數
        if (r == 0) {
            firstReader = current;
            firstReaderHoldCount = 1;
        } else if (firstReader == current) {
            firstReaderHoldCount++;
        } else {
            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;
    }
    //4. 處理在第二步中CAS操作失敗的自旋已經實現重入性
    return fullTryAcquireShared(current);
}



//讀鎖釋放
protected final boolean tryReleaseShared(int unused) {
    Thread current = Thread.currentThread();
    // 前面還是爲了實現getReadHoldCount等新功能
    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;
    }
}

       ④鎖降級如何實現:寫鎖 降級 爲讀鎖;

           遵循規則:按照獲取寫鎖,獲取讀鎖  再釋放寫鎖的次序,寫鎖能夠降級成爲讀鎖;  不支持鎖升級

void processCachedData() {
        rwl.readLock().lock();
        if (!cacheValid) {
            // 獲取寫鎖之前必須釋放讀鎖
            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;
          }
          // 降級通過在釋放寫鎖之前獲取讀鎖
          rwl.readLock().lock();
        } finally {
          rwl.writeLock().unlock(); // 解鎖寫,仍然保持讀
        }
      }
 
      try {
        use(data);
      } finally {
        rwl.readLock().unlock();
      }
    }
}

 

 

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