1. 程式人生 > >Java多執行緒之ReentrantLock實現原理和原始碼分析(二)

Java多執行緒之ReentrantLock實現原理和原始碼分析(二)

章節概覽、

1、ReentrantLock概述

ReentrantLock字面含義是可重入的互斥鎖,實現了和synchronize關鍵字一樣的獨佔鎖功能。但是ReentrantLock使用的是自旋鎖,通過CAS硬體原語指令實現的輕量級的鎖,不會引起上下文切換。而Synchronize關鍵字是重量級的且是獨佔的悲觀鎖。在使用過程中,會引起上下文切換。同時ReentrantLock增加了一些高階的擴充套件功能,比如它可以實現公平鎖,同時也可以繫結多個Conditon。每個Condition都維護了自己的阻塞佇列。而不像wait/notify只維護一個佇列而引發的喚醒錯誤的情況。

2、ReentrantLock的實現模板

ReentrantLock鎖保證執行緒安全的實現模板如下:

class X {
   private final ReentrantLock lock = new ReentrantLock();
   public void m() {
      lock.lock();  // block until condition holds
      try {
        // ... method body
      } finally {
        lock.unlock()
      }
    }
  }}

從預設的實現模板可以看出,其使用方式和Synchronize不同,首先其不需要同步的物件。而Synchronize需要一個同步鎖物件。其次ReentrantLock是顯示呼叫。使用者自己可以控制從哪裡開始,到哪裡結束。而Synchronize只能同步程式碼塊等。具體ReentrantLock的使用方式,請參考:

可重入鎖:ReentrantLock理解使用

3、ReentrantLock類圖結構

ReentrantLock的類圖結構如下:
在這裡插入圖片描述
從類的繼承圖上可以看出,ReentrantLock實現了Lock介面。同時其具有3個內部類,分別為Sync,NonfairSync,FairSync。而Sync有繼承了AbstractQueuedSynchronize,這個就是我們常說的AQS。而NonFairSync繼承Sync也就是我們所說的非公平鎖。FairSync繼承Sync實現了公平鎖功能。

3.1、Lock介面原始碼描述
/**
 * @see ReentrantLock
 * @see Condition
 * @see ReadWriteLock
 *
 * @since 1.5
 * @author Doug Lea
 */
public interface Lock {
    //獲取鎖
    void lock();
    //獲取可中斷鎖
    void lockInterruptibly() throws InterruptedException;
    //嘗試獲取鎖,立刻返回
    boolean tryLock();
    //在指定的時間範圍內嘗試獲取鎖,立即返回
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    //釋放當前鎖
    void unlock();
    //獲取一個條件物件,用於執行緒等待喚醒
    Condition newCondition();
}

3.2、ReentrantLock的構造方法和成員

ReentranLock的構造方法成員引數如下:

public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
   // 同步器的引用
    private final Sync sync;
   //非公平鎖的建構函式
    public ReentrantLock() {
        sync = new NonfairSync();
    }
    // 公平鎖的建構函式
	public ReentrantLock(boolean fair) {
	// 三目運算子,如果為true 則為公平鎖,反之為非公平鎖
        sync = fair ? new FairSync() : new NonfairSync();
    }
}
3.3、Sync抽象類核心原始碼分析
// 靜態的抽象內部類,修飾符為default,僅能在當前類所在的包中使用
// 繼承了AbstractQueuedSynchronizer抽象類
abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;
        
        /**
         * 抽象方法,獲取當前鎖,具體實現在子類中實現
         */
        abstract void lock();

        /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         * 非公平鎖嘗試的去獲取鎖,立刻返回
         */
        final boolean nonfairTryAcquire(int acquires) {
        // 獲取當前執行緒
            final Thread current = Thread.currentThread();
        // 獲取當前系統的同步狀態變數state
            int c = getState();
        // 判斷當前的系統狀態變數是否為0
        // 其中0,表示當前鎖空閒。大於0表示當前系統鎖已經被佔用
            if (c == 0) {
            // 利用CAS原語設定當前state的狀態值為1,立刻返回
                if (compareAndSetState(0, acquires)) {
                // 如果設定成功,表示已經獲取當前鎖,設定當前鎖的擁有者為當前執行緒
                    setExclusiveOwnerThread(current);
                 // 返回獲取成功
                    return true;
                }
            }
            // 如果當前是state不為0,判斷當前執行緒是否已經擁有鎖
            // 因為ReentrantLock是可衝入鎖,所以如果當前執行緒已經擁有鎖的情況下,可以再次使用該鎖
            else if (current == getExclusiveOwnerThread()) {
            // 如果當前執行緒已經擁有鎖,則把當前的 state + 1
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
            // 設定當前state的值
                setState(nextc);
                // 返回成功
                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) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
	
	// 判斷當前執行緒是否擁有鎖
        protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }
	// 返回條件物件
        final ConditionObject newCondition() {
            return new ConditionObject();
        }
        
        // Methods relayed from outer class
        // 獲取當前擁有鎖的執行緒
        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }
	// 獲得當前執行緒持有的可重入鎖的狀態值
        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }
		// 是否獲得鎖
        final boolean isLocked() {
            return getState() != 0;
        }

        /**
         * Reconstitutes the instance from a stream (that is, deserializes it).
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }
3.4、NonfairSync類核心原始碼分析

NonfairSync主要是非公平鎖的實現:

 static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         * 實現Sync的方法
         */
        final void lock() {
        // 嘗試獲取鎖lock() 方法
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
            // 獲取鎖失敗,加入到等待佇列
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
3.5、FairSync類核心原始碼分析

以下是公平鎖實現的核心原始碼

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
        // 公平鎖沒有嘗試的去獲取鎖的功能,直接呼叫acquire()方法
        final void lock() {
            acquire(1);
        }
        
        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            // 公平鎖中
            if (c == 0) {
            // 通過hasQueuedPredecessors判斷當前執行緒是否為head的next節點。
            // 如果是的話,則嘗試獲取鎖
            // 獲取鎖成功進行一系列的設定
                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原始碼分析:

public final boolean hasQueuedPredecessors() {
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        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());
    }

這裡的邏輯判斷稍微有些複雜,我們整理下思路。
return返回的程式碼,可以將邏輯判斷分為2部分,只要其中有一個為true,則返回為true。當返回為真的話, if (!hasQueuedPredecessors() && compareAndSetState(0, acquires))就失敗。我們詳細分析下:

  • h != t && ((s = h.next) == null

這個邏輯成立的一種可能是head指向頭結點,tail此時還為null。考慮這種情況:當其他某個執行緒去獲取鎖失敗,需構造一個結點加入同步佇列中(假設此時同步佇列為空),在新增的時候,需要先建立一個無意義傀儡頭結點(在AQS的enq方法中,這是個自旋CAS操作),有可能在將head指向此傀儡結點完畢之後,還未將tail指向此結點。很明顯,此執行緒時間上優於當前執行緒,所以,返回true,表示有等待中的執行緒且比自己來的還早。

  • s.thread != Thread.currentThread()

當前的執行緒和Head的next執行緒是否相等。如果不相等返回為true。說明當前執行緒不是head的next節點,因為在公平鎖中,只有head的next節點才有權利去獲取鎖

4、 結語

至此ReentrantLock的類結構和核心原始碼做了簡單分析。通過分析我們可以瞭解到ReentrantLock的整體結構,建構函式,以及當前類的成員變數。以及ReentrantLock的內部類的情況,內部類的繼承情況。ReentrantLock中使用了大量的內部類,很好的做到了封裝性。對細節實現部分進行了封裝。使用者只需要呼叫構造方法,而不用去關係其內部的實現情況。同時也是設計模式中的迪米特法則:最少知道原則的體現。實現了很好的高內聚減少了耦合。