【Java并发编程】ReentrantLock 源码解读

一. 介绍

上篇文章介绍了Lock接口,这篇文章我们就来看看实现Lock接口的ReentrantLock类



二. 属性

ReentrantLock只有两个属性

    private static final long serialVersionUID = 7373984872572414699L;
		// 提供了所有实现机制
    private final Sync sync;

Sync是ReentrantLock的一个内部抽象类,它继承了AQS,而ReentrantLock另外两个内部类:FairSync和NonFairSync则同时继承了Sync,并实现了Sync的lock方法。

总结来说,这三个内部类共同实现了AQS的所有钩子方法,并实现了一个Lock方法,而后面我们会看到,ReentrantLock类中实现的Lock接口的方法都是通过调用属性sync中的方法来实现的



三. 构造方法

ReentrantLock有两个构造方法

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

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

无参构造方法是创建了非公平锁,而我们也可以通过传入boolean变量来指定创建公平锁。非公平锁和公平锁有什么区别呢?

当一个线程在请求非公平锁的时候,如果锁此时被释放出来,则这个线程无需排队就可以直接获得锁,简称插队获取锁。那这样有啥好处呢?

提高锁资源的利用率,因为唤醒挂起的线程到该线程获得锁是需要时间的,这段时间锁是处于空闲状态,如果我们此时请求公平锁的线程能直接获得这把锁,就把这个浪费的时间利用起来了,虽然对已经排队的线程不太公平,所以也叫非公平锁



四. Lock接口方法的实现

4.1 Lock

公平锁实现

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

非公平锁的实现

final void lock() {
  	// 如果cas操作能够将state置为1,则说明此时锁空闲,如果抢锁成功,则将独占线程设置为自己;如果锁此时不空闲,则调用acquire方法
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

非公平锁在调用lock方法后会直接通过cas操作抢锁,如果成功则将独占线程置为自己,否则就调用AQS的acquire方法;而公平锁则直接调用acquire方法

我们在《AQS(1)—并发三板斧》中说过,AQS已经将acquire的大部分逻辑实现了,只留下了一个tryAcquire方法让子类重写,我们现在就来看下公平锁和非公平锁实现的tryAcquire方法

4.1.1 FairSync实现的tryAcquire
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
  	// 获取锁的状态
    int c = getState();
    // 为0说明锁空闲
    if (c == 0) {
      	// 通过hasQueuePredecessors方法判断同步队列是否为空,如果为false则说明为空,则我们再通过cas去获取锁,如果也成功,则将当前线程设置为独占线程,并返回true
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
  	// 如果c>0则锁已经被使用
    // 如果此时占用锁的线程就是本线程,说明本线程已经拿到锁(可重入锁)
    // 最后更新state返回true
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0        if (nexextct< 0 <)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    // 拿锁不成功返回false
    return false;
}

4.1.2 NonfairSync实现的tryAcquire
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) {
        // 与公平锁tryAcquire唯一的不同就是在这没有判断同步队列是否还有等待线程
        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;
}

非公平锁tryAcquire方法是调用的Sync方法的nonfairTryAcquire,而nonfairTryAcquire与公平锁的tryAcquire唯一的不同就在于如果此时锁空闲,就不会再去管同步队列中是否还有等待线程,而是直接抢锁,也就是我们第三节所说的公平锁与非公平锁的区别


4.2 LockInterruptibly

LockInterruptibly和lock方法一样,也是阻塞获取锁,但不同于lock方法,它会响应中断

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

可以看到,LockInterruptibly调用的sync的acquireInterruptibly,接下继续看acquireInterruptibly方法

public final void acquireInterruptibly(int arg)
        throws InterruptedException {
  	// 查看标志位判断是否被中断
    if (Thread.interrupted())
        throw new InterruptedException();
    if (!tryAcquire(arg))
        doAcquireInterruptibly(arg);
}

可以看到,进入方法,我们首先会判断此线程是否被中断过,如果被中断,则立马抛出中断异常,如果没有,我们则会通过上小节讨论过的tryAcquire方法去获取锁,如果获取失败,我们则调用doAcquireInterruptibly方法

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

是不是很眼熟?对,这个方法其实就是将AQS中addWaiter方法和acquireQueued方法合成了一个方法,然后去掉了判断是否被中断过的返回值,加上了抛出异常中断,大家去之前的文章中对比看下即可,这里就不冗述了


4.3 tryLock

tryLock是非阻塞的获取锁,不管成功与失败,都会立即返回结果,我们看下源码

public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

我们可以看到,tryLock调用的是Sync抽象类中的nonfairTryAcquire方法,这个方法在前面已经详细的讨论过,这里就不再赘述

可能会有小伙伴有疑问,nonfairTryAcquire这个方法是不管同步队列的情况,直接去抢锁,为什么公平锁的tryLock方法也要调用这个方法。这个问题很好,因为tryLock方法是Lock接口的方法,所以我们在现实时,是需要根据这个接口的语义规范去实现的,所以公平锁要用这个tryLock方法,那就需要实现这个语义

4.4 tryLock(long timeout, TimeUnit unit)

tryLock是立即返回结果,而tryLock(long timeout, TimeUnit unit)是带有超时时间的,则可能会阻塞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();
    return tryAcquire(arg) ||
        doAcquireNanos(arg, nanosTimeout);
}

可以看到,tryLock(long timeout, TimeUnit unit)调用的是Sync的tryAcquireNanos方法,而此方法首先会判断线程是否被中断,如果被中断过,则会抛出中断异常,否则就会调用tryAcquire或者doAcquireNanos方法来获取锁

tryAcquire在上面已经说过了,我们来看下doAcquireNanos方法,其实doAcquireNanos方法也是由AQS实现的

我们可以看到,doAcquireNanos方法和上面说的doAcquireInterruptibly很像,只是多了对时间的检查(红色框部分),且有返回值,如果是获取到了锁就返回true,而超时了就返回false。

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

unlock调用的是Sync的release的,而sync的release是继承了AQS的release方法,这个已经在《AQS—独占锁的释放》中详细阐述过了,这里不再赘述

4.6 newCondition
final ConditionObject newCondition() {
    return new ConditionObject();
}

与unlock同理,newCondtition也是继承使用了AQS的newCondititon方法,前文《AQS—ConditionObject》也已经详细阐述



五. 总结

其实通过对ReentrantLock的解读,我们可以看到AQS的重要性,其中许多方法都是继承使用了AQS的方法,所以如果大家想学好java并发编程,则AQS是必须深入了解的。

(完)





欢迎大家关注我的公众号 “程序员进阶之路”,里面记录了一个非科班程序员的成长之路 。

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值