可重入锁和递归锁ReentrantLock

概念

可重入锁就是递归锁

指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取到该锁的代码。
在同一线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。

也就是说:线程可以进入任何一个它已经拥有的锁所同步的代码块

ReentrantLock / Synchronized 就是一个典型的可重入锁

代码

可重入锁就是,在一个method1方法中加入一把锁,方法2也加锁了,那么他们拥有的是同一把锁

public synchronized void method1() {method2();
}public synchronized void method2() {}

也就是说我们只需要进入method1后,那么它也能直接进入method2方法,因为他们所拥有的锁,是同一把锁。

作用

可重入锁的最大作用就是避免死锁

可重入锁验证

证明Synchronized

/*** 可重入锁(也叫递归锁)* 指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取到该锁的代码,在同一线程在外层方法获取锁的时候,在进入内层方法会自动获取锁** 也就是说:`线程可以进入任何一个它已经拥有的锁所同步的代码块`*//*** 资源类*/
class Phone {/*** 发送短信* @throws Exception*/public synchronized void sendSMS() throws Exception{System.out.println(Thread.currentThread().getName() + "\t invoked sendSMS()");// 在同步方法中,调用另外一个同步方法sendEmail();}/*** 发邮件* @throws Exception*/public synchronized void sendEmail() throws Exception{System.out.println(Thread.currentThread().getId() + "\t invoked sendEmail()");}
}
public class ReenterLockDemo {public static void main(String[] args) {Phone phone = new Phone();// 两个线程操作资源列new Thread(() -> {try {phone.sendSMS();} catch (Exception e) {e.printStackTrace();}}, "t1").start();new Thread(() -> {try {phone.sendSMS();} catch (Exception e) {e.printStackTrace();}}, "t2").start();}
}

在这里,我们编写了一个资源类phone,拥有两个加了synchronized的同步方法,分别是sendSMS 和 sendEmail,我们在sendSMS方法中,调用sendEmail。最后在主线程同时开启了两个线程进行测试,最后得到的结果为:

t1    invoked sendSMS()
t1   invoked sendEmail()
t2   invoked sendSMS()
t2   invoked sendEmail()

这就说明当 t1 线程进入sendSMS的时候,拥有了一把锁,同时t2线程无法进入,直到t1线程拿着锁,执行了sendEmail 方法后,才释放锁,这样t2才能够进入

t1    invoked sendSMS()      t1线程在外层方法获取锁的时候
t1   invoked sendEmail()    t1在进入内层方法会自动获取锁t2    invoked sendSMS()      t2线程在外层方法获取锁的时候
t2   invoked sendEmail()    t2在进入内层方法会自动获取锁

证明ReentrantLock

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** 资源类*/
class Phone implements Runnable{Lock lock = new ReentrantLock();/*** set进去的时候,就加锁,调用set方法的时候,能否访问另外一个加锁的set方法*/public void getLock() {lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t get Lock");setLock();} finally {lock.unlock();}}public void setLock() {lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t set Lock");} finally {lock.unlock();}}@Overridepublic void run() {getLock();}
}
public class ReenterLockDemo {public static void main(String[] args) {Phone phone = new Phone();/*** 因为Phone实现了Runnable接口*/Thread t3 = new Thread(phone, "t3");Thread t4 = new Thread(phone, "t4");t3.start();t4.start();}
}

现在我们使用ReentrantLock进行验证,首先资源类实现了Runnable接口,重写Run方法,里面调用get方法,get方法在进入的时候,就加了锁

    public void getLock() {lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t get Lock");setLock();} finally {lock.unlock();}}

然后在方法里面,又调用另外一个加了锁的setLock方法

    public void setLock() {lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t set Lock");} finally {lock.unlock();}}

最后输出结果我们能发现,结果和加synchronized方法是一致的,都是在外层的方法获取锁之后,线程能够直接进入里层

t3    get Lock
t3   set Lock
t4   get Lock
t4   set Lock

当我们在getLock方法加两把锁会是什么情况呢? (阿里面试)

    public void getLock() {lock.lock();lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t get Lock");setLock();} finally {lock.unlock();lock.unlock();}}

最后得到的结果也是一样的,因为里面不管有几把锁,其它他们都是同一把锁,也就是说用同一个钥匙都能够打开

当我们在getLock方法加两把锁,但是只解一把锁会出现什么情况呢?

public void getLock() {lock.lock();lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t get Lock");setLock();} finally {lock.unlock();lock.unlock();}
}

得到结果

t3    get Lock
t3   set Lock

也就是说程序直接卡死,线程不能出来,也就说明我们申请几把锁,最后需要解除几把锁

当我们只加一把锁,但是用两把锁来解锁的时候,又会出现什么情况呢?

    public void getLock() {lock.lock();try {System.out.println(Thread.currentThread().getName() + "\t get Lock");setLock();} finally {lock.unlock();lock.unlock();}}

这个时候,运行程序会直接报错

t3    get Lock
t3   set Lock
t4   get Lock
t4   set Lock
Exception in thread "t3" Exception in thread "t4" java.lang.IllegalMonitorStateExceptionat java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457)at com.moxi.interview.study.thread.Phone.getLock(ReenterLockDemo.java:52)at com.moxi.interview.study.thread.Phone.run(ReenterLockDemo.java:67)at java.lang.Thread.run(Thread.java:745)
java.lang.IllegalMonitorStateExceptionat java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457)at com.moxi.interview.study.thread.Phone.getLock(ReenterLockDemo.java:52)at com.moxi.interview.study.thread.Phone.run(ReenterLockDemo.java:67)at java.lang.Thread.run(Thread.java:745)

非法监视器状态异常

Java面试之锁-可重入锁和递归锁相关推荐

  1. 第二季:5公平锁/非公平锁/可重入锁/递归锁/自旋锁谈谈你的理解?请手写一个自旋锁【Java面试题】

    第二季:5值传递和引用传递[Java面试题] 前言 推荐 值传递 说明 题目 24 TransferValue醒脑小练习 第二季:5公平锁/非公平锁/可重入锁/递归锁/自旋锁谈谈你的理解?请手写一个自 ...

  2. Java并发编程-ReentrantLock可重入锁

    目录 1.ReentrantLock可重入锁概述 2.可重入 3.可打断 4.锁超时 5.公平锁 6.条件变量 Condition 1.ReentrantLock可重入锁概述 相对于 synchron ...

  3. 测试Lock锁-可重入锁(Java)

    测试Lock锁-可重入锁(Java) package src.thread;import java.util.concurrent.locks.ReentrantLock;public class T ...

  4. Java 可重入锁 不可重入锁

    文章目录 Java 可重入锁 & 不可重入锁 概述 论证synchronized是可重入锁: 论证Lock是可重入锁: 自定义不可重入锁: Java 可重入锁 & 不可重入锁 概述 可 ...

  5. 谈谈java并发锁(重入锁、读写锁、公平锁)

    目录 重入锁 简单重入锁 重入锁的等待通知(Condition) 多Condition 公平锁和非公平锁 读写锁ReentrantReadWriteLock 锁优化总结: 重入锁和读写锁,他们具有比s ...

  6. 年轻人,看看 Redisson 分布式锁—可重入锁吧!太重要了

    作者 | 李祥    责编 | 张文 来源 | 企鹅杏仁技术站(ID:xingren-tech) 引言 作为后端开发,对于所谓的线程安全.高并发等一系列名词肯定都不会陌生,相关的一些概念及技术框架是面 ...

  7. 年轻人,看看Redisson分布式锁—可重入锁吧!太重要了

    1.引言 作为后端开发,对于所谓的线程安全.高并发等一系列名词肯定都不会陌生,相关的一些概念及技术框架是面试中的宠儿,也是工作中解决一些特定场景下的技术问题的银弹.今天我们就来聊聊这些银弹中的其中一枚 ...

  8. 可重入锁 不可重入锁_什么是可重入锁?

    可重入锁 不可重入锁 在Java 5.0中,增加了一个新功能以增强内部锁定功能,称为可重入锁定. 在此之前,"同步"和"易失性"是实现并发的手段. public ...

  9. 可重入锁/不可重入锁,公平锁/非公平锁,乐观锁/悲观锁,独享锁/共享锁,偏向锁/轻量级锁/重量级锁,分段锁,自旋锁

    在并发编程中,会涉及到各种各样的锁,这篇文章主要介绍各种锁的分类以及作用. 介绍的内容如下: 可重入锁/不可重入锁 公平锁/非公平锁 乐观锁/悲观锁 独享锁/共享锁 偏向锁/轻量级锁/重量级锁 分段锁 ...

  10. RocketMQ的broker处理消息commit时,加锁应该使用自旋锁还是重入锁

    讨论的主题 以下内容基于rocketmq4.7.1版本,并且集群模型采用的是DLedger 2主4从,主从节点在不同的机架上,所以关于其中提到的压测数据请勿直接套用自己的环境,仅供参考. rocket ...

最新文章

  1. MyBatis-Spring(四)--MapperFactoryBean实现增删改查
  2. ajax 更新模型数据_PyTorch视觉工具包torchvision重大更新!支持各种检测模型、分割模型,还有许多数据集...
  3. Matlab矩阵的产生
  4. easiest approach for improving writing skills for ielts
  5. python数码时钟代码_python时钟的实现
  6. 【安全牛学习笔记】抓包嗅探
  7. hdu 1286找新朋友 (简单数学题)
  8. mysql性能优化 洪斌_洪斌 - MySQL性能诊断与实践
  9. Android的Context 安卓常用系统服务(当前运行包名/当前网络状态和开关网络/音频服务)...
  10. java中说明书/开发文档如何编写?
  11. html添加自动视频播放器,html5教程:[3]video标签轻松添加视频
  12. 设为首页 加入收藏 html,如何在网站上添加“设为首页”“加入收藏”
  13. 软考高级 真题 2011年上半年 信息系统项目管理师 综合知识
  14. eds能谱图分析实例_电子产品质量的提升,离不开这些失效分析!
  15. iPhone 13不会自动锁屏该怎么解决?
  16. 【CG物理模拟系列】流体模拟--粒子法之MPS法(理论)
  17. Node.js 小白入门课3-设置基本数据库
  18. 软件测试人员需不需要懂代码?需要什么技能才行?
  19. 真实IP收集及其利用方式
  20. Python test1

热门文章

  1. docker 日志_解决docker容器日志导致主机磁盘空间满了的情况
  2. matlab+text+extent,MATLAB字体及特殊符号标示方法
  3. 准备写个Spring Boot教程
  4. Mouse Detected Problem
  5. Spring框架中的控制反转和依赖注入
  6. 一些需要烂熟于心的代码
  7. CF788E:New task
  8. C语言-05内存剖析
  9. oracle权限的分配
  10. 一个全球主要城市天气预报的WebService.