当前位置:网站首页>ReentrantLock工作原理
ReentrantLock工作原理
2022-08-02 02:36:00 【季风泯灭的季节】
AQS原理分析
什么是AQS
java.util.concurrent包中的大多数同步器实现都是围绕着共同的基础行为,比如等待队列、条件队列、独占获取、共享获取等,而这些行为的抽象就是基于AbstractQueuedSynchronizer(简称AQS)实现的,AQS是一个抽象同步框架,可以用来实现一个依赖状态的同步器。JDK中提供的大多数的同步器如Lock, Latch, Barrier等,都是基于AQS框架来实现的。
- 一般是通过一个内部类Sync继承 AQS
- 将同步器所有调用都映射到Sync对应的方法

AQS具备的特性:
- 阻塞等待队列
- 共享/独占
- 公平/非公平
- 可重入
- 允许中断
AQS内部维护属性volatile int state
- state表示资源的可用状态
- State三种访问方式:
- getState()
- setState()
- compareAndSetState()
AQS定义两种资源共享方式
- Exclusive-独占,只有一个线程能执行,如ReentrantLock
- Share-共享,多个线程可以同时执行,如Semaphore/CountDownLatch
AQS定义两种队列
- 同步等待队列: 主要用于维护获取锁失败时入队的线程
- 条件等待队列: 调用await()的时候会释放锁,然后线程会加入到条件队列,调用signal()唤醒的时候会把条件队列中的线程节点移动到同步队列中,等待再次获得锁
ReentrantLock
ReentrantLock是一种基于AQS框架的应用实现,是JDK中的一种线程并发访问的同步手段,它的功能类似于synchronized是一种互斥锁,可以保证线程安全。相对于 synchronized,ReentrantLock具备如下特点:
- 可中断
- 可以设置超时时间
- 可以设置为公平锁
- 支持多个条件变量
- 与 synchronized 一样,都支持可重入
synchronized和ReentrantLock的区别:
- synchronized关键字,是JVM层次的锁实现,ReentrantLock是类,是JDK层次基于AQS的锁实现;二者都是可重入的独占锁。
- synchronized的锁状态是无法在代码中直接判断的,但是ReentrantLock可以通过 ReentrantLock#isLocked判断;
- synchronized自动加解锁,ReentrantLock要手动加解锁,它的操作更加灵活。
- synchronized是非公平锁,ReentrantLock是可以是公平也可以是非公平的;ReentrantLock要保证公平性也会引入额外的开销,导致吞吐量下降,慎用。
- synchronized是不可以被中断的,而ReentrantLock#lockInterruptibly方法是可以被中断的;
- 在发生异常时synchronized会自动释放锁,而ReentrantLock需要开发者在finally块中显示释放锁;
- ReentrantLock获取锁的形式有多种:如立即返回是否成功的tryLock(),以及等待指定时长的获取,更加灵活;
- synchronized在特定的情况下对于已经在等待的线程是后来的线程先获得锁(回顾一下sychronized的唤醒策略),而ReentrantLock对于已经在等待的线程是先来的线程先获得锁;
- 在低竞争情况下synchronized的性能优于ReentrantLock。在高并发下,synchronized操作monitor涉及线程的用户态、内核态的切换,性能不如ReentrantLock。
ReentrantLock的使用
同步执行,类似于synchronized
private static int sum = 0;
private static Lock lock = new ReentrantLock();
//private static TulingLock lock = new TulingLock();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(()->{
//加锁
lock.lock();
try {
// 临界区代码
// TODO 业务逻辑:读写操作不能保证线程安全
for (int j = 0; j < 10000; j++) {
sum++;
}
} finally {
// 解锁
lock.unlock();
}
});
thread.start();
}
Thread.sleep(2000);
System.out.println(sum);
}
可重入
public static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
method1();
}
public static void method1() {
lock.lock();
try {
log.debug("execute method1");
method2();
} finally {
lock.unlock();
}
}
public static void method2() {
lock.lock();
try {
log.debug("execute method2");
method3();
} finally {
lock.unlock();
}
}
public static void method3() {
lock.lock();
try {
log.debug("execute method3");
} finally {
lock.unlock();
}
}
可中断
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
log.debug("t1启动...");
try {
lock.lockInterruptibly();
try {
log.debug("t1获得了锁");
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
log.debug("t1等锁的过程中被中断");
}
}, "t1");
lock.lock();
try {
log.debug("main线程获得了锁");
t1.start();
//先让线程t1执行
Thread.sleep(1000);
t1.interrupt();
log.debug("线程t1执行中断");
} finally {
lock.unlock();
}
锁超时
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
log.debug("t1启动...");
// 注意: 即使是设置的公平锁,此方法也会立即返回获取锁成功或失败,公平策略不生效
// if (!lock.tryLock()) {
// log.debug("t1获取锁失败,立即返回false");
// return;
// }
//超时
try {
if (!lock.tryLock(1, TimeUnit.SECONDS)) {
log.debug("等待 1s 后获取锁失败,返回");
return;
}
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
try {
log.debug("t1获得了锁");
} finally {
lock.unlock();
}
}, "t1");
lock.lock();
try {
log.debug("main线程获得了锁");
t1.start();
//先让线程t1执行
Thread.sleep(2000);
} finally {
lock.unlock();
}
公平锁
//ReentrantLock lock = new ReentrantLock(true); //公平锁
ReentrantLock lock = new ReentrantLock(); //非公平锁
for (int i = 0; i < 500; i++) {
new Thread(() -> {
lock.lock();
try {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug(Thread.currentThread().getName() + " running...");
} finally {
lock.unlock();
}
}, "t" + i).start();
}
// 1s 之后去争抢锁
Thread.sleep(1000);
for (int i = 0; i < 500; i++) {
new Thread(() -> {
lock.lock();
try {
log.debug(Thread.currentThread().getName() + " running...");
} finally {
lock.unlock();
}
}, "强行插入" + i).start();
}
条件变量
private static ReentrantLock lock = new ReentrantLock();
private static Condition cigCon = lock.newCondition();
private static Condition takeCon = lock.newCondition();
private static boolean hashcig = false;
private static boolean hastakeout = false;
//送烟
public void cigratee(){
lock.lock();
try {
while(!hashcig){
try {
log.debug("没有烟,歇一会");
cigCon.await();
}catch (Exception e){
e.printStackTrace();
}
}
log.debug("有烟了,干活");
}finally {
lock.unlock();
}
}
//送外卖
public void takeout(){
lock.lock();
try {
while(!hastakeout){
try {
log.debug("没有饭,歇一会");
takeCon.await();
}catch (Exception e){
e.printStackTrace();
}
}
log.debug("有饭了,干活");
}finally {
lock.unlock();
}
}
public static void main(String[] args) {
ReentrantLockDemo6 test = new ReentrantLockDemo6();
new Thread(() ->{
test.cigratee();
}).start();
new Thread(() -> {
test.takeout();
}).start();
new Thread(() ->{
lock.lock();
try {
hashcig = true;
log.debug("唤醒送烟的等待线程");
cigCon.signal();
}finally {
lock.unlock();
}
},"t1").start();
new Thread(() ->{
lock.lock();
try {
hastakeout = true;
log.debug("唤醒送饭的等待线程");
takeCon.signal();
}finally {
lock.unlock();
}
},"t2").start();
}
ReentrantLock源码
加锁:
- CAS加锁,成功后将重入线程设置为当前线程。重入数加1。
- 如果加锁失败则将线程放入一个双向链表的等待队列,并调用unsafe.park()挂起线程。
解锁:
- 直接将重入线程设置为null,将state还原为0。
- 调用unsafe.park()唤起等待队列中的一个线程去竞争锁。
边栏推荐
- nacos startup error, the database has been configured, stand-alone startup
- [ORB_SLAM2] SetPose, UpdatePoseMatrices
- Electronic Manufacturing Warehouse Barcode Management System Solution
- The principle and code implementation of intelligent follower robot in the actual combat of innovative projects
- Good News | AR opens a new model for the textile industry, and ALVA Systems wins another award!
- 数仓:为什么说 ETL 的未来不是 ELT,而是 EL (T)
- 【web】Understanding Cookie and Session Mechanism
- 1688API
- 优炫数据库导库导错了能恢复吗?
- 2022-08-01 反思
猜你喜欢
随机推荐
isa指针使用详情
架构:应用架构的演进以及微服务架构的落地实践
【Unity入门计划】2D Game Kit:初步了解2D游戏组成
leetcode / anagram in string - some permutation of s1 string is a substring of s2
Flask 报错:WARNING This is a development server. Do not use it in a production deployment
KICAD 拉线宽度无法修改,解决方法
240...循迹
A good book for newcomers to the workplace
指针数组和数组指针
JVM调优实战
ofstream,ifstream,fstream read and write files
Qt自定义控件和模板分享
【 wheeled odometer 】
2022-08-01 Install mysql monitoring tool phhMyAdmin
AI target segmentation capability for fast video cutout without green screen
2022 NPDP take an examination of how the results?How to query?
Flask入门学习教程
2022河南青训联赛第(三)场
openGauss切换后state状态显示不对
BI - SQL 丨 WHILE