java多線程之ReentrantLock公平鎖源碼

前言

本篇是基於JDK8版本,分析ReentrantLock公平鎖的獲取和釋放的源碼。

1、獲取公平鎖

final void lock() {
	//ReentrantLock是可重入鎖,所以單個線程每次進入都+1,初始值是0
     acquire(1);
}
public final void acquire(int arg) {
	//這裏總共有4個方法,下面會分別進行詳細分析
   	if (!tryAcquire(arg) &&
       acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
       selfInterrupt();
}

1.1、tryAcquire方法

tryAcquire方法的作用就是嘗試去獲取鎖,獲取成功返回true,獲取失敗則返回false。

protected final boolean tryAcquire(int acquires) {
		//獲取當前線程
       final Thread current = Thread.currentThread();
       //獲取鎖狀態,就是每次線程進入都要加1的那個值
       int c = getState();
       //c等於0,就是初始狀態,鎖沒有被佔用
       if (c == 0) {
       		//判斷隊列中當前線程是否是第一個
       		//CAS再次判斷鎖是否沒有被佔用
       		//設置鎖被當前線程佔用,然後返回
           if (!hasQueuedPredecessors() &&
               compareAndSetState(0, acquires)) {
               setExclusiveOwnerThread(current);
               return true;
           }
       }
       else if (current == getExclusiveOwnerThread()) {
       		//如果鎖的擁有者是當前線程
       		//鎖的狀態+1
           int nextc = c + acquires;
           if (nextc < 0)
               throw new Error("Maximum lock count exceeded");
          //更新鎖的狀態,然後返回
           setState(nextc);
           return true;
       }
       return false;
}

1.2、addWaiter方法

addWaiter方法是創建當前線程的Node節點,並在Node中記錄鎖是“獨佔鎖”類型,並且將該節點添加到隊列的末尾,當前線程就處於等待狀態了,我們先看看Node節點的源碼:

private transient volatile Node head;    // CLH隊列的隊首
private transient volatile Node tail;    // CLH隊列的隊尾

// 隊列的Node節點
static final class Node {
    static final Node SHARED = new Node();
    static final Node EXCLUSIVE = null;

    // 線程已被取消,對應的waitStatus的值
    static final int CANCELLED =  1;
    // 當前線程的後一個線程需要被喚醒,對應的waitStatus值
    static final int SIGNAL    = -1;
    // 線程休眠,對應的waitStatus的值
    static final int CONDITION = -2;
    // 其它線程獲取到“共享鎖”,對應的waitStatus的值
    static final int PROPAGATE = -3;
    
    volatile int waitStatus;
    // 前一節點
    volatile Node prev;
    // 後一節點
    volatile Node next;
    // 節點所對應的線程
    volatile Thread thread;

    // 標記隊列是獨佔鎖還是共享鎖
    Node nextWaiter;

    //共享鎖則返回true,獨佔鎖則返回false。
    final boolean isShared() {
        return nextWaiter == SHARED;
    }

    // 返回前一節點
    final Node predecessor() throws NullPointerException {
        Node p = prev;
        if (p == null)
            throw new NullPointerException();
        else
            return p;
    }
}

看完Node源碼,下面我們再看看addWaiter方法

private Node addWaiter(Node mode) {
		//創建當前線程Node節點
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        //判斷隊列是否爲空,不爲空則添加到隊列末尾
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //爲空,則新建一個隊列,並添加進去
        enq(node);
        return node;
}

1.3、acquireQueued方法

acquireQueued方法是在隊列中獲取鎖

final boolean acquireQueued(final Node node, int arg) {
	//標記acquire是否成功
     boolean failed = true;
     try {
     	//標記是否被掛起過,後面是否要執行中斷方法
         boolean interrupted = false;
         //死循環,正常情況下線程只有獲得鎖才能跳出循環
         for (;;) {
             final Node p = node.predecessor();
             //當前節點是頭節點,並且獲取鎖成功,才能退出循環,這才體現出公平鎖的“公平”
             if (p == head && tryAcquire(arg)) {
             	//設置頭節點
                 setHead(node);
                 p.next = null; // help GC
                 failed = false;
                 return interrupted;
             }
             if (shouldParkAfterFailedAcquire(p, node) &&
                 parkAndCheckInterrupt())//判斷是否要阻塞當前線程,如果要阻塞掛起,則interrupted爲true,表示曾經中斷過
                 interrupted = true;
         }
     } finally {
         if (failed)
         //如果tryAcquire出現異常那麼取消當前結點的獲取
             cancelAcquire(node);
     }
}

1.4、selfInterrupt方法

這個方法很簡單,就是中斷當前線程

static void selfInterrupt() {
    Thread.currentThread().interrupt();
}

2、釋放公平鎖

public void unlock() {
	//這裏的1和獲取鎖的1是相同的,釋放鎖的時候是-1
     sync.release(1);
}

2.1、release方法

public final boolean release(int arg) {
	//tryRelease來嘗試釋放當前線程持有的鎖。
	//成功的話,則喚醒後繼等待線程,並返回true。否則,直接返回false
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
        	//喚醒當前線程的後繼線程
            unparkSuccessor(h);
        return true;
    }
    return false;
}

2.2、unparkSuccessor方法

private void unparkSuccessor(Node node) { 
	//獲取當前線程的狀態
	int ws = node.waitStatus;
	if (ws < 0)
		compareAndSetWaitStatus(node, ws, 0);
	//循環獲取當前節點的後繼節點狀態小於等於0的節點
	Node s = node.next;
	if (s == null || s.waitStatus > 0) {
		s = null;
		for (Node t = tail; t != null && t != node; t = t.prev)
			if (t.waitStatus <= 0)
				s = t;
	}
	if (s != null)
		//喚醒後繼對應節點
		LockSupport.unpark(s.thread);
}

結束語

本篇詳細的分析了ReentrantLock獲取公平鎖和釋放公平鎖的代碼,大部分的代碼上面都進行了中文註釋。下一篇將介紹ReentrantLock的非公平鎖的獲取和釋放。

分析源碼不易,如果有幫助到你請隨手點個贊,謝謝大家!

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