ReentrantLock源码详解


前言

ReentrantLock是基于AQS独占模式实现的可重入互斥锁,可重入语义表示当前线程已经获取到了锁还可以再次获取锁。与synchronized关键字的语义相同,在java1.5ReentrantLock可以替代synchronized,因为java1.5ReentrantLock的性能更好。1.6之后优化了synchronized,使得synchronized关键字的性能大幅度提升了,所以1.6之后不建议使用ReentrantLock来替代synchronized。但ReentrantLock具备一些扩展的特性,例如:可以指定公平锁(synchronized是非公平锁),轮询锁,定时锁等都是ReentrantLock所特有的。那么什么是公平锁和非公平锁呢?源码给你答案。
既然是基于AQS实现的,那么就得解决两个问题:
1、AQS中的state变量对于ReentrantLock来说意味着什么?
2、如何实现更改state的方法:tryAcquire、tryRelease


一、内部结构

简单使用

private Lock lock = new ReentrantLock();

public void testLock(){
    lock.lock();
    try{
        // do what you want
    }finally {
        lock.unlock(); // 注意:解锁一定要放在finally块中
    }
}

1.构造函数

public ReentrantLock() { // 默认非公平锁
   sync = new NonfairSync();
}

public ReentrantLock(boolean fair) {// 也可以指定公平锁
    sync = fair ? new FairSync() : new NonfairSync();
}

2.主要方法

// 获取锁,忽略中断
public void lock() {
    sync.lock();
}
// 获取锁,响应中断
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
// 尝试获取锁
public boolean tryLock() {
 return sync.nonfairTryAcquire(1);
}
// 尝试获取锁,但有时间限制
public boolean tryLock(long timeout, TimeUnit unit)
      throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
// 释放锁
public void unlock() {
    sync.release(1);
}

在这里插入图片描述
实现了Lock接口,ReentrantLock这个类的内部结构如下:ReentrantLock有三个内部类,Sync继承了AQS,FairSync和NonfairSync都继承了Sync。ReentrantLock仅仅是封装了AQS,然后实现了AQS中tryAcquire(int args)和tryRelease(int releases)方法。其中tryRelease(int releases)是在Sync类中实现,tryAcquire(int args)是在FairSync和NonfairSync中实现。而ReentrantLock中的方法都是通过Sync实现的。

// 继承自AQS
private final Sync sync;

abstract static class Sync extends AbstractQueuedSynchronizer {

    abstract void lock();
	/**
	* 这个方法就能很直观看出state在ReentrantLock的语义了,不为0就表示上锁了。
	*/
    final boolean isLocked() {
        return getState() != 0;
    }

    /**
     * 非公平尝试获取锁,就是将state从0变到1(获取锁),或者从1变到n(可重入语义)
     * 单单看非公平获取锁可能还不知道非公平和公平的区别,等看了公平获取锁源码对比一下就清楚了
     * tryLock方法也是调用的这个方法
     */
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();// 获取AQS中state的值
        if (c == 0) {// 说明锁还是自由状态
            if (compareAndSetState(0, acquires)) {// 直接通过cas获取锁,如果为true则说明获取成功
                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);// 因为已经获取到锁了,所以不用通过CAS来操作
            return true;
        }
        return false;
    }
	/**
	* 释放锁,state从1到0或者从n到n-1的过程,只有state=0时才表示释放锁成功
	*/
    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() {
        return getExclusiveOwnerThread() == Thread.currentThread();
    }
    // 创建条件对象
    final ConditionObject newCondition() {
        return new ConditionObject();
    }
   	// 获取锁所属线程
    final Thread getOwner() {
        return getState() == 0 ? null : getExclusiveOwnerThread();
    }
	// 获取持有的数量
    final int getHoldCount() {
        return isHeldExclusively() ? getState() : 0;
    }
}

二、公平锁与非公平锁

非公平锁的代码非常简单,当前线程想要获取锁,先直接CAS尝试获取锁看下能否获取得到,不需要管等待队列中是否有阻塞线程

1.NonfairSync

static final class NonfairSync extends Sync {
    final void lock() {
        if (compareAndSetState(0, 1)) // 先直接通过cas获取锁
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);// 这是AQS中独占模式的获取锁方法,会至少调用一次tryAcquire方法
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

2.FairSync

static final class FairSync extends Sync {
    final void lock() {// 对比一下区别,公平锁少了一次CAS操作
        acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (!hasQueuedPredecessors() && // 这里多了一个判断等待队列中是否有阻塞线程,没有阻塞线程才CAS尝试获取,否则直接返回false
                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); // 因为已经获取到了锁,所以不需要CAS
            return true;
        }
        return false;
    }
}
/**
* 这个方法是AQS类中的,判断队列中是否有等待线程,如果返回true表示队列中有,并且第一个等待线程不是当前线程,如果返回false,则表示没有等待线程
*/
public final boolean hasQueuedPredecessors() {
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    // head==tail则表示没有等待线程
    // s.thread == Thread.currentThread()表示当前线程是第一个等待线程,则返回false。
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

公平锁和非公平锁源码阅读完之后,我们来总结一下区别。
代码层面区别有两个地方:
1、lock方法非公平锁会先CAS尝试获取锁
2、tryAcquire方法公平锁会先调用hasQueuedPredecessors方法判断等待队列中是否有阻塞线程
公平锁线程每次需要获取锁先会去等待队列中看看有没有等待的线程。非公平锁则不管是否有等待队列,等待队列中是否有阻塞线程,直接通过CAS来尝试获取锁。
那么问题来了?
1、请问为什么ReentrantLock和synchronized都是默认非公平锁?公平锁和非公平锁哪个性能更优?为什么?
2、既然非公平锁性能更优那什么场景下需要用到公平锁呢?
大家可以思考一下。

三、Condition

方法都在AQS中。

public Condition newCondition() {
    return sync.newCondition();
}

总结

深入研究了ReentrantLock的核心源码,关键有以下几点:
1、理解state在ReentrantLock中的含义,0代表什么,1代表什么,n又代表什么?
2、什么是公平锁和非公平锁?
3、可重入语义?tryAcquire和tryRelease方法逻辑?
4、Condition理解和实现

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值