ReentrantLock源码详解

ReentrantLock是Java中常用的锁工具,今天我们来了解一下。本篇文章设计到AQS,如果了解到不太清楚的话,可以看我之前写的文章AQS源码详细分析,让你掌握AQS原理,独占锁、共享锁、Condition

ReentrantLock概述

ReentrantLock是基于我们之前讲过的AQS构造的。ReentrantLock是一个独占锁,提供了非公平锁和公平锁两种模式。

我们首先说下公平锁和非公平锁的区别:
公平锁:线程在请求锁的时候,依次入队,严格按照先来后到的顺序分配锁。
非公平锁:线程在请求锁的时候,如果锁可用,就能申请,申请不到才会入队。

通过构造参数传入true或false来控制是否公平锁,true为公平锁,空构造方法默认生成非公平锁。

public ReentrantLock(boolean fair) {
    //FairSync对应着公平锁
    //NonfairSync对应着公平锁
    //二者都继承于Sync类,而Sync继承于AQS抽象类
    sync = fair ? new FairSync() : new NonfairSync();
}
public ReentrantLock() {
    sync = new NonfairSync();
}

在这里插入图片描述

我们来通过源码来分析公平锁和非公平锁的申请流程的区别:
公平锁:

final void lock() {
    acquire(1);
}

public final void acquire(int arg) {
    //如果获得锁失败,就将线程入队,之前详细说过,不在说了。
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        //队列为空或者当前线程是head的后继节点,并且CAS申请锁成功
        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;
}

public final boolean hasQueuedPredecessors() {
        
	 Node t = tail; 
	 Node h = head;
	 Node s;
	 //判断队列非空并且当前线程不是head的后继节点,只有head的后继节点才能申请锁,head是个哨兵空节点
	 return h != t &&
	     ((s = h.next) == null || s.thread != Thread.currentThread());
}

从代码可以看出,只有当队列为空的时候,线程才会直接获得锁,其他情况就直接入队

非公平锁

final void lock() {
    //不管三七二十一,上来就是申请锁。
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        //获得不到锁,执行acquire
        acquire(1);
}

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

protected final boolean tryAcquire(int acquires) {
    //非公平锁
    return nonfairTryAcquire(acquires);
}

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;
}

加锁方式

ReentrantLock提供了三种加锁方式

加锁方法 模式介绍
tryLock() 按照非公平锁申请,只请求一次,不会阻塞
tryLock(long timeout, TimeUnit unit) 设置超时时间来申请锁,最多阻塞timeout个unit秒,可以响应中断
lockInterruptibly() 阻塞式申请锁,抢锁的时候响应中断
lock() 阻塞式的申请锁,抢锁的时候不响应中断

tryLock

public boolean tryLock() {
    return sync.nonfairTryAcquire(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;
}

从上述代码,我们可以看出tryLock()只会试着抢一次锁,成功就成功,不成功就拉倒!

tryLock(long timeout, TimeUnit unit)


//TimeUnit说时间单位,timeout为数量
public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

public final boolean tryAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
    if (Thread.interrupted())
        //通过抛出异常来响应中断
        throw new InterruptedException();
        //tryAcquire(arg)先申请一次,如果申请成功就直接返回
        //申请不成功,就执行doAcquireNanos
    return tryAcquire(arg) ||
        doAcquireNanos(arg, nanosTimeout);
}

private boolean doAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    //如果时间小于0,返回false
    if (nanosTimeout <= 0L)
        return false;
    //最后期限
    final long deadline = System.nanoTime() + nanosTimeout;
    //向锁queue中加入node
    final Node node = addWaiter(Node.EXCLUSIVE);
	//下面的代码之前我们在AQS文章中都讲过,忘记的可以温习一下。
    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 true;
            }
            nanosTimeout = deadline - System.nanoTime();
            if (nanosTimeout <= 0L)
                return false;
            //如果剩余时间大于阈值的话,就将线程挂起一下
            if (shouldParkAfterFailedAcquire(p, node) &&
                nanosTimeout > spinForTimeoutThreshold)
                //挂起nanosTimeout时间,到时间如果没有被唤醒就自动苏醒
                LockSupport.parkNanos(this, nanosTimeout);
             //如果线程在抢锁期间中断了,就抛出异常
            if (Thread.interrupted())
                throw new InterruptedException();
        }
    } finally {
		//出现异常后,node设置为cancel状态,放弃锁
        if (failed)
            cancelAcquire(node);
    }
}

从上述代码看,tryLock(long timeout, TimeUnit unit)是在规定时间内申请锁,是响应中断的。具体做法是先会无脑申请一次锁,申请失败就会一直自旋CAS申请,如果剩余时间大于阈值,就将线程挂起,阻塞时间是剩余的时间。

lock()

public void lock() {
    sync.lock();
}

lock就是我们之前说公平锁和非公平锁的时候列举的代码,会一直阻塞申请锁,在申请锁期间不会响应中断,抢锁成功后才会处理中断。

lockInterruptibly()

public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}

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);
    }
}

从代码中可以看出,lockInterruptibly()是一直阻塞的申请锁,但是会响应中断

unlock()

public void unlock() {
    sync.release(1);
}

release将锁释放,并且唤醒锁queue中的head的next节点来争夺锁。
这个我们之前在AQS中独占锁的释放中详细说过了,不再详细叙述了。

new Condition()

这个之前我们在AQS中也说过了,不在详细叙述,感兴趣的可以看文章开头的AQS的链接

posted @ 2021-08-22 10:10  张孟浩Jay  阅读(117)  评论(1编辑  收藏  举报