ReentrantLock的功能详解与理解
创始人
2024-04-06 06:35:29

👨‍💻个人主页: 才疏学浅的木子
🙇‍♂️ 本人也在学习阶段如若发现问题,请告知非常感谢 🙇‍♂️
📒 本文来自专栏: Java基础
❤️ 支持我:👍点赞 🌹收藏 🤟关注

ReentrantLock

  • AQS
  • ReentrantLockd概述
  • 加锁流程
  • 可重入原理
  • 可打断原理
  • 公平锁原理
  • 条件变量实现原理

AQS

全称是AbstractQueuedSynchronizer,是阻塞锁和相关的同步器工具的框架

特点
1、用state属性来表示资源的状态(分为独享状态与共享模式)
getState - 获取state状态
setState - 设置state状态
compareAndSetState - 乐观锁机制设置state状态
独占模式是只有一个线程能够访问资源,而共享模式允许多个线程访问资源
2、提供了FIFO的等待队列,类似于Monitor的EntryList
3、条件变量来实现等待、唤醒机制,支持多个条件变量,类似Monitor的WaitSet

子类主要实现这些方法

tryAcquire 获取锁
tryRelease 释放锁
tryAcquireShared
tryReleaseShared
isHeldExclusively

ReentrantLockd概述

相比synchronized具备如下特点
1、可中断
2、可以设置超时时间
3、可以设置为公平锁
4、支持多个条件变量
与synchronized一样都支持可重入

可重入

可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁。
如果是不可重入锁,那么第二次获取锁的时候,自己也会被锁挡住

public class Test1 {public static void main(String[] args) {ReentrantLock lock = new ReentrantLock();lock.lock();try {System.out.println("lock1~");lock.lock();try {System.out.println("lock2~");}finally {lock.unlock();}}finally {lock.unlock();}}
}

在这里插入图片描述

可打断

在获取锁的时候可被打断避免一直阻塞等待获取锁

public class Test1 {public static void main(String[] args) {ReentrantLock lock = new ReentrantLock();Thread t1 = new Thread(() -> {try {lock.lockInterruptibly();System.out.println("lock1获取到了锁");} catch (InterruptedException e) {System.out.println("lock1中被打断了");}});lock.lock(); // 主线程获取到锁t1.start(); // t1线程启动//打断t1.interrupt();}
}

在这里插入图片描述

可超时

与可打断类似,只是打断是被动,超时是主动,超时了就自动不获取锁了

public class Test1 {public static void main(String[] args) {ReentrantLock lock = new ReentrantLock();Thread t1 = new Thread(() -> {try {boolean flag = lock.tryLock(5, TimeUnit.SECONDS);if (flag){System.out.println("lock1获取到了锁");}else {System.out.println("lock1没有获取到锁");}} catch (InterruptedException e) {e.printStackTrace();}});lock.lock(); // 主线程获取到锁t1.start(); // t1线程启动}
}

在这里插入图片描述

可设置公平锁

不容易代码实现
使用公平锁是为了解决饥饿但是会降低并发度

可设置多个条件变量

ReentrantLockd相比synchronized,ReentrantLockd可支持多个条件变量,这好比synchronized是那些不满足条件的线程都在一间休息室里面等,而ReentrantLockd支持多间休息室

await前需要获得锁
await执行后,会释放锁,进入condition等待
await的线程被唤醒去重新竞争锁
竞争锁成功后执行await后的

在这里插入图片描述

state 就是加锁状态
head 就是等待队列的头
tail 就是等待队列的尾
owner 就是当前是那个线程,全名为ExclusiveOwnerThread我这里简写的

加锁流程

最开始没有线程竞争,加锁成功

final void lock() {if (compareAndSetState(0, 1))// 加锁成功setExclusiveOwnerThread(Thread.currentThread());else// 加锁失败acquire(1);
}

在这里插入图片描述

第二个线程来竞争 CAS失败,进入acquire(1)

    public final void acquire(int arg) {// 尝试获取锁失败,所以!tryAcquire(arg)为true//acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 创建一个节点对象然后放入等待队列中if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}

进入acquireQueued方法

    final boolean acquireQueued(final Node node, int arg) {boolean failed = true;try {boolean interrupted = false;for (;;) {final Node p = node.predecessor();// 如果是第二个节点再尝试一次if (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // help GCfailed = false;return interrupted;}//前驱节点waitStatus改为-1代表有责任唤醒下一个节点if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)cancelAcquire(node);}}

蓝色三角形为Node是waitStatus状态,其中0为默认状态
Node的创建是懒惰的
其中第一个Node为哨兵或者哑元用来占位并不关联线程

在这里插入图片描述
在这里插入图片描述
然后当前线程Thread-1 阻塞住

在这里插入图片描述
假如有多个线程经历上述过程

在这里插入图片描述
Thread-0 释放锁

        protected final boolean tryRelease(int releases) {int c = getState() - releases; // 这就是可重入的原因,当前线程如果是获取锁的线程state++if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free = false;if (c == 0) {free = true;setExclusiveOwnerThread(null);}setState(c);return free;}

在这里插入图片描述
当前队列不为空唤醒下一个

    public final boolean release(int arg) {if (tryRelease(arg)) {Node h = head;if (h != null && h.waitStatus != 0)// 唤醒下一个节点unparkSuccessor(h);return true;}return false;}

唤醒Thread-1

在这里插入图片描述
因为我们这是非公平锁,如果Thread-1在获取的时候Thread-4来获取,Thread-4抢夺成功

在这里插入图片描述

可重入原理

加锁

        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()) { // 判断是否当前是当前线程是否是owner线程int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}

释放锁

        protected final boolean tryRelease(int releases) {int c = getState() - releases; // 释放锁state减少if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free = false;if (c == 0) {free = true;setExclusiveOwnerThread(null);}setState(c);return free;}

可打断原理

抛出异常

    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 GCfailed = false;return;}if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())// 抛出异常throw new InterruptedException();}} finally {if (failed)cancelAcquire(node);}}

公平锁原理

        protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {// hasQueuedPredecessors() 判断队列中是否有其它线程等待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;}}

条件变量实现原理

每个条件变量都对应着一个等待队列,其实现类就是ConditionObject

waitState标志位设置为-2
并且调用fullRelease方法释放线程上的锁,避免重入时候多把锁没释放完

在这里插入图片描述

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...