当前位置:网站首页>How ReentrantLock works
How ReentrantLock works
2022-08-02 02:45:00 【Season of monsoon die out】
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是类,是JDKHierarchy is based onAQS的锁实现;Both are reentrant exclusive locks.
- synchronized的锁状态是无法在代码中直接判断的,但是ReentrantLock可以通过 ReentrantLock#isLocked判断;
- synchronized自动加解锁,ReentrantLockTo unlock manually,Its operation is more flexible.
- synchronized是非公平锁,ReentrantLock是可以是公平也可以是非公平的;ReentrantLockTo ensure fairness also introduces additional overhead,导致吞吐量下降,慎用.
- synchronized是不可以被中断的,而ReentrantLock#lockInterruptibly方法是可以被中断的;
- 在发生异常时synchronized会自动释放锁,而ReentrantLock需要开发者在finally块中显示释放锁;
- ReentrantLock获取锁的形式有多种:如立即返回是否成功的tryLock(),and waiting fingersTimed acquisition,更加灵活;
- synchronized在特定的情况下对于已经在等待的线程是后来的线程先获得锁(回顾一下sychronized的唤醒策略),而ReentrantLockFor threads that are already waiting, it is the first-come thread先获得锁;
- in low competition situationssynchronized的性能优于ReentrantLock.在高并发下,synchronized操作monitorUser mode involving threads、内核态的切换,性能不如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加锁,Set the reentrant thread as the current thread on success.reentrancy number plus1.
- If the lock fails, the thread is put into a waiting queue of a doubly linked list,并调用unsafe.park()挂起线程.
解锁:
- Set the reentrant thread directly tonull,将state还原为0.
- 调用unsafe.park()Arouse a thread in the waiting queue to compete for the lock.
边栏推荐
猜你喜欢
随机推荐
MySQL - CRUD operations
* 比较版本号
JVM调优实战
PAT甲级打卡-1001-1004
递归检查配置项是否更变并替换
53. 最小的k个数
Install mysql using docker
FOFAHUB使用测试
KICAD 拉线宽度无法修改,解决方法
列表常用方法
JS中获取对象数据类型的键值对的键与值
2022 Henan Youth Training League Game (3)
永磁同步电机36问(三)——SVPWM代码实现
线程的不同状态
忽晴忽雨
Lombok
网络层解析——IP协议、地址管理、路由选择
What to study after the PMP exam?The soft exam ahead is waiting for you~
2022年NPDP考完多久出成绩?怎么查询?
2022-08-01 Install mysql monitoring tool phhMyAdmin