1. 程式人生 > 實用技巧 >執行緒基礎知識05- ReentrantLock重入鎖

執行緒基礎知識05- ReentrantLock重入鎖

     ReentrantLock也叫重入鎖,可以對同一執行緒資源進行重複加鎖。通過lock()方法可以顯式的加鎖,並且再次呼叫lock(),不會出現阻塞的情況

Sync子類提供鎖的基本實現機制

  • 非公平鎖的獲取

    • 獲取獨佔鎖後,增加狀態碼
 //加鎖
 final void lock() {
            if (compareAndSetState(0, 1))//如果是首次,增加鎖狀態碼為1
                setExclusiveOwnerThread(Thread.currentThread());
            else
                /**
                 * 呼叫AQS中的acquire方法,加入到同步佇列中
                 * 迴圈呼叫tryAcquire方法,當前節點的前驅是頭節點時,設定當前節點為頭節點
                 */    
                acquire(1);
        }


 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;
        }
  • 釋放鎖操作

    • 釋放鎖後更新減少狀態碼,同步狀態為0時,表示鎖釋放成功
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;
        }

公平鎖FairSync

  • 公平鎖的獲取鎖方法

    • 同步佇列的頭節點進行獲取鎖,如果成功,則返回true,如果未獲取成功,返回false
protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                 /**
                  * 1.當前執行緒節點為頭節點,並通過CAS成功將state設定成acuires值
                  * 返回true
                  */   
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
             /**
              * 當前執行緒和獨佔執行緒一致時
              * 1.更改狀態;
              * 2.返回獲取鎖成功  
              */    
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

 public final boolean hasQueuedPredecessors() {
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

非公平鎖NonfairSync

  • 直接呼叫SYN的nofairTryAcquire方法
 protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }

公平鎖和非公平鎖建立

  • 通過傳入true或false,初始化建立公平鎖或非公平鎖

  • 預設是不公平鎖

 public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

public ReentrantLock() {
        sync = new NonfairSync();
    }

公平鎖和非公平鎖的優缺點

  • 公平鎖:

    優點:保證了按照鎖的獲取的時間長短,時間最長的先獲取到鎖。會獲取當前節點所在等待佇列的位置,必須按照位置執行。

    缺點:

    • 因為要判斷前驅,且嚴格按照同步佇列順序,因此效率更低

    • 也因為順序執行,所以多次獲取鎖操作,會造成上下文切換冗餘,切換次數更多

  • 非公平鎖:

    優點: 因為只需要判斷狀態碼,因此,同一個鎖再次獲取到的概率比較大,所以鎖切換次數少,效率比較高。保證有更大的吞吐量。

    缺點: 同一個鎖的多次呼叫,容易造成,單個鎖呼叫頻繁,執行緒出現“飢餓”狀態;

(圖片摘自《Java併發程式設計藝術》)