大家好,我是栗子为
今天想和大家分享Java锁系列的一个经典问题,什么是可重入锁?
话不多说,我们一起来看看可重入锁的原理
01
—
什么是可重入锁
可重入锁又名递归锁,是指同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提是锁对象是同一个对象)
Java中synchronized和ReentrantLock都是可重入锁,在一定程度上可避免死锁
02
—
可重入锁的分类
隐式锁
synchronized关键字既可以修饰同步代码块,又可以修饰同步方法
在一个synchronized修饰的方法或代码块内部调用本类的其他synchronized修饰的方法或代码块时,是永远可以得到锁的
举个栗子
public static void main(String[] args) {
final Object object = new Object();
new Thread(()->{
synchronized (object){
System.out.println(Thread.currentThread().getName()+"外层加锁");
synchronized (object){
System.out.println(Thread.currentThread().getName()+"中层加锁");
synchronized (object){
System.out.println(Thread.currentThread().getName()+"内层加锁");
}
}
}
},"t1").start();
}
结果如下
t1外层加锁
t1中层加锁
t1内层加锁
我们可以看到,synchronized关键字默认就是可重入锁,不会发生死锁问题
显式锁
Lock类中也有ReentrantLock这样的可重入锁
03
—
可重入锁实现原理(synchronized)
-
每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针
-
当执行monitorenter时,如果目标锁对象的计数器为0,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1
-
在目标锁对象的计数器不为0的情况下,如果锁对象的持有线程是当前线程,那么Java虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁
-
当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1,计数器为0代表锁已被释放
举个栗子
public class Main{
static Lock lock = new ReentrantLock();
public static void main(String[] args) {
new Thread(()->{
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+"外层加锁");
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+"内层加锁");
}finally {
lock.unlock();
}
}finally {
lock.unlock();
}
},"t1").start();
}
}
结果如下
t1外层加锁
t1内层加锁
若此时在一个线程中,lock与unlock次数不匹配,导致计数器不为0,那么其他线程若想拿到锁,则需一直等待,所以为了避免此问题,在finally代码块中,执行lock.unlock()
方法
04
—
总结
可重入锁可以用一句话来解释:
一个线程中的多个流程可以获取同一把锁,持有这把同步锁可以再次进入,并且自己可以获取自己的内部锁。
好了,今天的内容就分享到这,小为也是出了好几期并发编程的专题了,希望能对大家有帮助
关注六只栗子,面试不迷路
作者 栗子为
编辑 一口栗子
原文始发于微信公众号(六只栗子):Java锁系列之可重入锁
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/88283.html