java多线程之ReentrantLock公平锁源码解析

前言

本篇是基于JDK8版本,分析ReentrantLock公平锁的获取和释放的源码。

1、获取公平锁

lock()方法在ReentrantLock.java的FairSync类中实现,它的源码如下:

final void lock() {
	//ReentrantLock是可重入锁,所以单个线程每次进入都+1,初始值是0
     acquire(1);
}

acquire()在AbstractQueuedSynchronizer(AQS)抽象类中定义的,它的源码如下:

public final void acquire(int arg) {
	//这里总共有4个方法,tryAcquire实现方法在FairSync类中
	//addWaiter、acquireQueued、selfInterrupt三个方法在AQS中实现
   	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的非公平锁的获取和释放。

分析源码不易,如果有帮助到你请随手点个赞,谢谢大家!

原创文章 55 获赞 76 访问量 17万+

猜你喜欢

转载自blog.csdn.net/cool_summer_moon/article/details/105996403