ReentrantLock(重入锁)简单源码分析
Posted ql211lin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ReentrantLock(重入锁)简单源码分析相关的知识,希望对你有一定的参考价值。
1.ReentrantLock是基于AQS实现的一种重入锁。
2.先介绍下公平锁/非公平锁
公平锁
- 公平锁是指多个线程按照申请锁的顺序来获取锁。
非公平锁
- 非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。
3.重入锁/不可重入锁
可重入锁:广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。
不可重入锁:不可重入锁,与可重入锁相反,不可递归调用,递归调用就发生死锁。
4.统一入口
//获取锁。 @Override public void lock() { sync.lock(); } //获取锁,响应中断 @Override public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } //仅在调用时锁未被另一个线程保持的情况下,才获取该锁。 @Override public boolean tryLock() { return sync.nonfairTryAcquire(1); } //如果锁在给定等待时间内没有被另一个线程保持,且当前线程未被 中断,则获取该锁。 @Override public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); } //试图释放此锁。 @Override public void unlock() { sync.release(1); }
ReentrantLock默认是非公平模式,可以通过构造函数指定模式。
/** * 构造函数,默认非公平锁 */ public ReentrantLock() { sync = new NonfairSync(); } /** * 构造函数,通过布尔参数设置是否是公平锁 * @param fair */ public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
4.非公平模式的加锁和解锁
加锁:
//获取锁。 @Override public void lock() { sync.lock(); }
然后调用NonfairSync的lock方法:
/** * 加锁 */ @Override final void lock() { //以cas方式尝试将AQS中的state从0更新为1 if (compareAndSetState(0, 1)) { //获取锁成功则将当前线程标记为持有锁的线程,然后直接返回 setExclusiveOwnerThread(Thread.currentThread()); } else { acquire(1); } }
如果设置state失败,则调用NonfairSync的tryAcquire方法
@Override protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }
然后调用Sync的nonfairTryAcquire方法:
/** * 非公平模式的情况下获取同步状态 * @param acquires * @return */ final boolean nonfairTryAcquire(int acquires) { //获取当前线程实例 final Thread current = Thread.currentThread(); //获取同步state变量的值,即当前锁被重入的次数 int c = getState(); //state为0,说明当前锁未被任何线程持有 if (c == 0) { //以cas方式获取锁 if (compareAndSetState(0, acquires)) { //将当前线程标记为持有锁的线程 setExclusiveOwnerThread(current); //获取锁成功,非重入 return true; } //当前线程就是持有锁的线程,说明该锁被重入了,实现重入 } else if (current == getExclusiveOwnerThread()) { //计算state变量要更新的值 int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); //非同步方式更新state值 setState(nextc); //获取锁成功,重入 return true; } //尝试获取锁失败 return false; }
这边体现了锁的重入,用state的次数表示锁被线程重入了state次。
else if (current == getExclusiveOwnerThread()) { //计算state变量要更新的值 int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); //非同步方式更新state值 setState(nextc); //获取锁成功,重入 return true; }
解锁:
//试图释放此锁。 @Override public void unlock() { sync.release(1); }
然后调用Sync的tryRelease方法:
/** * 释放同步状态 * @param releases * @return */ @Override protected final boolean tryRelease(int releases) { //计算待更新的state值 int c = getState() - releases; //判断当前线程是否独占 if (Thread.currentThread() != getExclusiveOwnerThread()) { throw new IllegalMonitorStateException(); } //是否释放同步状态成功的标志 boolean free = false; //待更新的state值为0,说明持有锁的线程未重入,一旦释放锁其他线程将能获取 if (c == 0) { free = true; //清除锁的持有线程标记 setExclusiveOwnerThread(null); } //更新state值 setState(c); return free; }
锁的最终释放要去锁对于获取进行计数自增,计算表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数为0时表示已经释放成功。
5.公平模式的加锁和解锁
加锁:
@Override protected final boolean tryAcquire(int acquires) { //获取当前线程实例 final Thread current = Thread.currentThread(); //获取同步state变量的值,即当前锁被重入的次数 int c = getState(); //state为0,说明当前锁未被任何线程持有 if (c == 0) { /** * 1.判断同步队列中当前节点是否有前驱节点 * 2. 以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"); //非同步方式更新state值 setState(nextc); //获取锁成功,重入 return true; } //尝试获取锁失败 return false; }
对于非公平锁,只要cas设置同步状态成功,则表示当前线程获取了锁,而公平锁则不同,公平锁的获取增加了hasQueuedPredecessors的判断,即加入了判断同步队列中当前节点是否有前驱节点,
如果返回true,则表示有线程比当前线程更早滴请求获取锁,因此需要等待前驱线程获取并释放锁之后才能继续获取锁。
解锁:同公平锁的解锁方式
以上是关于ReentrantLock(重入锁)简单源码分析的主要内容,如果未能解决你的问题,请参考以下文章
ReentrantLock核心源码分析,AQS独占模式,可重入锁