大家好,这里是淇妙小屋,一个分享技术,分享生活的博主0. Lock与AQS类图
以下是我的主页,各个主页同步更新优质博客,创作不易,还请大家点波关注
掘金主页
知乎主页
Segmentfault主页
开源中国主页
后续会发布更多MySQL,Redis,并发,JVM,分布式等面试热点知识,以及Java学习路线,面试重点,职业规划,面经等相关博客
转载请标明出处!
文章图片
1. Lock接口 1.1 Lock的定义
Lock接口定义了锁的API操作,用于实现java中锁机制
public interface Lock {//如果锁可用,则获得锁后返回
//如果锁不可用,那么当前线程会阻塞,直到获取锁后才会返回
void lock();
//可中断的获取锁(获得锁的过程可中断)
//如果锁可用,则获得锁后返回
//如果锁不可用,那么线程会阻塞获取锁,阻塞获取锁的过程是可中断的,受到中断会抛出InterruptedException
void lockInterruptibly() throws InterruptedException;
//非阻塞的尝试获得锁
//如果锁可用,获得锁后,返回true
//如果锁不可用,返回false
boolean tryLock();
//尝试获取锁(获得锁的过程可中断)
//如果锁可用,获得锁后,返回true
//如果锁不可用,那么在指定时间内会不断的尝试获得锁
//如果成功获得锁——返回true
//如果被中断——抛出InterruptedException
//如果超时——返回false
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
//释放锁
void unlock();
//获取Condition对象,Condition对象与当前Lock对象绑定,当前线程只有获得了锁
//才能调用该Condition对象的await()方法,而调用后,当前线程将释放锁,进入Conditon对象的等待队列中WATING
//当其他线程调用Condition对象的signal(),才会唤醒Condition对象的等待队列中的WATING线程
Condition newCondition();
}
1.2 Lock与sychronized的异同
- 共同点
- Lock的子类中有可重入锁
- 区别
- Lock显式的获取锁与释放锁
sychronized隐式的获取锁与释放锁
- Lock只能给代码块上锁
sychronized可以给代码块,方法上锁
- Lock依赖JDK实现
sychronized依赖JVM实现
- Lock有独占模式与共享模式,每个模式还分公平锁与非公平锁
sychronized是独占模式的非公平锁
- Lock是可中断的,线程在获得锁的过程中是可以影响中断
sychronized不可中断,线程在阻塞等待锁的释放的时候,是不会响应中断的
- Lock可以设定超时时间,超时会返回
sychronized不行
- Lock显式的获取锁与释放锁
//Condition对象——1.相当于一个等待队列2.必须与一个锁实例绑定3.一个锁实例可以有多个Condition对象
//相当于monitor对象的WaitSet
//必须持有Condition对象绑定的锁,才可以调用其方法
public interface Condition {//相当于Object.wait(),可中断
//持有锁的线程释放锁,进入等待队列中,线程状态更变为WATING,直到以下2种情况醒来
//1.有其他线程调用等待队列的signal()或signalAll()
//2.被其他线程中断
//醒来后的线程会从等待队列移动到锁实例的同步队列,状态由WAITING更改为BLOKING
void await() throws InterruptedException;
//同await(),但是不可中断
void awaitUninterruptibly();
//同 await(),但是多了一种唤醒情况——等待超时(单位纳秒)
long awaitNanos(long nanosTimeout) throws InterruptedException;
//同 await(),但是多了一种唤醒情况——等待超时
boolean await(long time, TimeUnit unit) throws InterruptedException;
//同 await(),但是多了一种唤醒情况——等待超时
boolean awaitUntil(Date deadline) throws InterruptedException;
//将等待队列中的首节点移动到锁实例的同步队列,然后通过LockSupport唤醒线程
void signal();
//将等待队列中的全部节点移动到锁实例的同步队列,然后通过LockSupport唤醒线程
void signalAll();
}
2.2 Condition与monitor的区别
- Condition——等待队列中等待的线程是可响应中断的
monitor——等待队列中等待的线程不可响应中断
- Condition————等待队列中的线程可以指定等待到未来的某个具体时间点
monitor——不支持
AbstractQueuedSynchronizer抽象队列同步器——用于构建锁或其他同步组件的基础框架
子类通过继承AQS并实现它的抽象方法来实现锁
AQS支持两种模式——独占模式,共享模式
3.2 AQS结构
文章图片
AOS的核心字段——exclusiveOwnerThread
- 独占模式下,持有锁的线程
- state用于表示同步器的状态(可称为同步状态)
- AQS的不同子类对state的运用不同
- 对于ReentrantLock
- state=0——表示同步器没有被占用(没有线程持有锁)
state!=0——表示同步器已被占用(有线程正在使用锁)
- 尝试获取锁——检查state的值是否为0,如果为0尝试用CAS修改其值,如果不为0—则未取得锁
尝试释放锁——将state的值CAS修改为0
- state=0——表示同步器没有被占用(没有线程持有锁)
- 对于CountDownLatch,CyclicBarrier
- state有不同的运用
- 对于ReentrantLock
- CANCELLED(1):同步队列中的节点被中断或者超时
- INITIAL(0):初始化状态
- SIGNAL(-1):
- CONDITION(-2):表示节点在等待队列中
- PROPAGATE(-3):
- 独占模式
- boolean tryAcquire()
- boolean tryRelease()
- boolean IsHeldExclusively()
- 共享模式
- boolean tryAcquireShared()
- boolean tryReleaseShared()
AQS独占模式下
- 同步队列的首节点持有锁
- 如果线程尝试获取锁失败,那么会封装成一个Node,然后加入同步队列,并开始自旋
- 节点从同步队列移除的条件——前继节点为首节点 and 成功获得锁
- 节点释放锁时——会唤醒其后继节点
文章图片
同步队列中,只有当节点的前继节点是首节点,才能尝试取得锁,原因如下
- 首节点是取得锁的节点,首节点释放锁后,会唤醒其后继节点
后继节点被唤醒后需要检查自己的前继节点是否为首节点
- 维护同步队列的FIFO原则
文章图片
3.3.3 超时获取锁 过程基本与获取锁相同
区别在于
- 每次自旋操作,在判断自己是否需要被阻塞之前,会优先判断是否已超时,如果超时了,就返回false
- 如果需要被阻塞,还会检查剩余的时间有没有大于阈值
如果大于阈值——通过LockSupport让线程进行超时阻塞
LockSupport.parkNanos(this, nanosTimeout);
3.4.1 共享模式与独占模式的区别 共享模式与独占模式的区别在于——同一时刻是否有多个线程可以同时获取到锁
文章图片
- 共享模式访问资源——其他共享式访问允许,独占式访问不允许
- 独占模式访问资源——其他访问一律不允许,被阻塞
文章图片
3.4.3 共享模式释放锁
文章图片
4. ReentrantLock实现AQS的独占模式 4.1 ReentrantLock介绍
可重入:任意线程获得锁后能够再次获取该锁而不会被锁阻塞
ReentrantLock实现了AQS的独占模式,是一个可重入锁,还分为 公平锁与 非公平锁
- 公平锁:先对锁进行获取请求的线程一定先获得锁
- 非公平锁
非公平锁可能出现 线程饥饿问题——部分线程迟迟无法获得资源
【看完这篇,Lock和AQS就是弟弟】
文章图片
ReentrantLock大多数方法的实现都是Sync及其子类来完成,ReentrantLock只是对外暴露了接口
4.2 ReentrantLock获得锁
4.2.1 非公平锁
文章图片
4.2.2 公平锁
文章图片
4.2.3 公平锁与非公平锁的不同 FairSync和NonfairSync的 lock() 和 tryAcquire() 逻辑不同
- 非公平锁在lock()方法的开始就会尝试去通过CAS修改同步状态以获得锁,公平锁不会
- 在自旋时,非公平锁和公平锁都会在前继节点为同步队列首节点时,调用tryAcquire()尝试获取锁
在 tryAcquire()中,如果state为0,那么非公平锁不会关心节点在同步队列中的位置,直接尝试CAS修改state获得锁;但是非公平锁关心节点的位置,会检查是否有前继节点,如果有,就会放弃
4.3 ReentrantLock释放锁
公平锁释放锁与非公平锁释放锁采用同一个逻辑
文章图片
推荐阅读
- java|IOS 自动化测试
- 大数据|二、JDK安装与变量配置
- java|ubuntu docker遇到的一些问题
- java|Ubuntu下单机安装Hadoop详细教程(附所需安装包下载)
- 数据结构与java集合|java集合图解源码系列【4】(从HashMap讲到红黑树和哈希表)
- Java编程开发|java开发(Class.forName 和 ClassLoader的区别和联系 | 使用场景 | 多方位解析)
- #|Zookeeper后端开发工具Curator的使用 | Curator对节点的增删改查 | ACL权限控制 | 分布式锁 | 分布式计数器 | 附带最新版本下载
- Java编程开发|Java多线程(synchronized | Volatile 和Lock和ReadWriteLock多方位剖析(一))
- java|springBoot自动装配原理