来源:blog.csdn.net/xiewenfeng520/article/details/107230996

前言

只对死锁代码感兴趣的可以直接跳到第三小节 必然死锁示例,如果对死锁还不太了解的,我们可以一起来讨论以下几个议题

  • 什么是死锁?
  • 死锁有什么危害和特点?
  • 代码实现一个必然死锁的示例
  • 分析死锁的过程

1.什么是死锁?

关键词:并发场景,多线程

首先我们需要知道,死锁一定发生在并发场景中。我们为了保证线程安全,有时会给程序使用各种能保证并发安全的工具,尤其是锁,但是如果在使用过程中处理不得当,就有可能会导致发生死锁的情况。

关键词:互不相让

死锁是一种状态,当两个(或多个)线程(或进程)相互持有对方所需要的资源,却又都不主动释放自己手中所持有的资源,导致大家都获取不到自己想要的资源,所有相关的线程(或进程)都无法继续往下执行,在未改变这种状态之前都不能向前推进,我们就把这种状态称为死锁状态,认为它们发生了死锁。

简而言之,死锁就是两个或多个线程(或进程)被无限期地阻塞,相互等待对方手中资源的一种状态。

两个线程死锁的情况

如图所示,线程1 已经持有了 锁1,同时 线程2 也已经持有了锁2,然后 线程1 尝试获取 锁2,但是 线程2 并没有释放 锁2,所以 线程1 处于阻塞状态,同理可知,图中的 线程2 获取 锁1也会被阻塞。

这样一来,线程1 和 线程2 就发生了死锁,因为它们都相互持有对方想要的资源,却又不释放自己手中的资源,形成相互等待,而且会一直等待下去。

2.死锁的影响和危害

2.1 死锁的影响

死锁的影响在不同系统中是不一样的,影响的大小一部分取决于当前这个系统或者环境对死锁的处理能力。

2.1.1 数据库中

例如,在数据库系统软件的设计中,考虑了监测死锁以及从死锁中恢复的情况。在执行一个事务的时候可能需要获取多把锁,并一直持有这些锁直到事务完成。在某个事务中持有的锁可能在其他事务中也需要,因此在两个事务之间有可能发生死锁的情况,一旦发生了死锁,如果没有外部干涉,那么两个事务就会永远的等待下去。

但数据库系统不会放任这种情况发生,当数据库检测到这一组事务发生了死锁时,根据策略的不同,可能会选择放弃某一个事务,被放弃的事务就会释放掉它所持有的锁,从而使其他的事务继续顺利进行。

此时程序可以重新执行被强行终止的事务,而这个事务现在就可以顺利执行了,因为所有跟它竞争资源的事务都已经在刚才执行完毕,并且释放资源了。

2.1.2 JVM 中

在 JVM 中,对于死锁的处理能力就不如数据库那么强大了。如果在 JVM 中发生了死锁,JVM 并不会自动进行处理,所以一旦死锁发生,就会陷入无穷的等待。

2.2 死锁的危害以及特点

关键词:概率性事件

死锁的问题和其他的并发安全问题一样,是概率性的,也就是说,即使存在发生死锁的可能性,也并不是 100% 会发生的。如果每个锁的持有时间很短,那么发生冲突的概率就很低,所以死锁发生的概率也很低。但是在线上系统里,可能每天有几千万次的“获取锁”、“释放锁”操作,在巨量的次数面前,整个系统发生问题的几率就会被放大,只要有某几次操作是有风险的,就可能会导致死锁的发生。

也正是因为死锁“不一定会发生”的特点,导致提前找出死锁成为了一个难题。压力测试虽然可以检测出一部分可能发生死锁的情况,但是并不足以完全模拟真实、长期运行的场景,因此没有办法把所有潜在可能发生死锁的代码都找出来。

关键词:危害大,发生几率不高

一旦发生了死锁,根据发生死锁的线程的职责不同,就可能会造成 子系统崩溃、性能降低 甚至 整个系统崩溃 等各种不良后果。而且死锁往往发生在高并发、高负载的情况下,因为可能会直接影响到很多用户,造成一系列的问题。以上就是死锁发生几率不高但是危害大的特点。

3.必然死锁示例

public class MustDeadLockDemo {public static void main(String[] args) {Object lock1 = new Object();Object lock2 = new Object();new Thread(new DeadLockTask(lock1, lock2, true), "线程1").start();new Thread(new DeadLockTask(lock1, lock2, false), "线程2").start();}static class DeadLockTask implements Runnable {private boolean flag;private Object lock1;private Object lock2;public DeadLockTask(Object lock1, Object lock2, boolean flag) {this.lock1 = lock1;this.lock2 = lock2;this.flag = flag;}@Overridepublic void run() {if (flag) {synchronized (lock1) {System.out.println(Thread.currentThread().getName() + "->拿到锁1");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "->等待锁2释放...");synchronized (lock2) {System.out.println(Thread.currentThread().getName() + "->拿到锁2");}}}if (!flag) {synchronized (lock2) {System.out.println(Thread.currentThread().getName() + "->拿到锁2");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "->等待锁1释放...");synchronized (lock1) {System.out.println(Thread.currentThread().getName() + "->拿到锁1");}}}}}
}

执行结果:

可以看到程序一直处于阻塞状态。

4.过程分析

其实上面的代码示例发生死锁的过程就是第一小节中 两个线程发生死锁 的情况,这里我们把图拿过来,方便分析。

本文使用 IDEA 进行调试,将断点打在 33 行,run方法的第一行,选择 Thread 模式。

注意:调试过程,因为有人为的等待时间,所以并不会发生死锁,这里只是演示线程执行的顺序和状态。

第一步,线程1进入,flag = true,进入第一个 synchronized 同步块,拿到 lock1(锁1)

第二步,直接点击 Resume Program(F9),进入线程2,此时 flag = false,进入第二个 synchronized 同步块

当然如果 Thread.sleep 的时间够长,或者操作速度够快的话,也能发生死锁。

5.总结

本章我们讨论了什么是死锁,以及死锁的影响和危害,演示了一个必然死锁的例子,然后使用 IDEA 工具调试了两个线程发生死锁的步骤。

在 JVM 中如果发生死锁,可能会导致程序部分甚至全部无法继续向下执行的情况,所以死锁在 JVM 中所带来的危害和影响是比较大的,我们需要尽量避免。

最后如果在面试中碰到这一题,希望大家都能顺利通过。

参考:《Java 并发编程 78 讲》- 徐隆曦

近期热文推荐:

1.1,000+ 道 Java面试题及答案整理(2021最新版)

2.别在再满屏的 if/ else 了,试试策略模式,真香!!

3.卧槽!Java 中的 xx ≠ null 是什么新语法?

4.Spring Boot 2.5 重磅发布,黑暗模式太炸了!

5.《Java开发手册(嵩山版)》最新发布,速速下载!

觉得不错,别忘了随手点赞+转发哦!

面试官:手写一个必然死锁的例子?一顿操作猛如虎。。相关推荐

  1. Chat Top10 | 给面试官手写一个 Nacos,多少 K?

    每周推荐的最新 Chat Top10 没有固定主题,仅仅是编辑部参考多方评分和反馈挑选出来的好文章,不一定适合你的口味,建议小心食用- 我们一起看下第三期 Chat Top10 都有哪些内容 ???? ...

  2. Java多线程学习四十:如何写一个必然死锁的例子

    死锁是什么?有什么危害? 什么是死锁 发生在并发中 首先你要知道,死锁一定发生在并发场景中.我们为了保证线程安全,有时会给程序使用各种能保证并发安全的工具,尤其是锁,但是如果在使用过程中处理不得当,就 ...

  3. Java 匹马行天下之一顿操作猛如虎,框架作用知多少?,HTTPS 面试常问全解析

    学了 Spring,就来学 Spring 的第一个子项目叫 SpringMVC,一看名字,MVC,这个在学 JavaWeb 的时候听过吧,SpringMVC 其实就是处理 Servlet 里的那些内容 ...

  4. ajax put请求_前端基础面试:手写一个ajax,说说XMLHttpRequest 都有哪些属性?

    前言 在 Ajax 出现之前,网页想要和服务器通信,最常用的方式是使用 form 表单:用户提交表单后,浏览器就开始跳转,服务器接收表单并处理,然后将新的网页返回给浏览器:整个过程用户都只有等待,用户 ...

  5. mysql死锁 简单例子_写一个Mysql死锁的例子

    创建表 CREATE TABLE `test1` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(10) NOT NULL, PRIMAR ...

  6. ds查找—二叉树平衡因子_面试官让我手写一个平衡二叉树,我当时就笑了

    平衡二叉树对于初学者一直是一个比较复杂的知识点,因为其里面涉及到了大量的旋转操作.把大量的同学都给转晕了.这篇文章最主要的特点就是通过动画的形式演示.确保大家都能看懂.最后是手写一个平衡二叉树. 一. ...

  7. @cacheable 设置过期时间_缓存面试三连击——聊聊Redis过期策略?内存淘汰机制?再手写一个LRU 吧!...

    大家好,今天我和大家想聊一聊有关redis的过期策略的话题. 听到这里你也许会觉得:"我去,我只是个日常搬砖的,这种偏底层的知识点,我需要care吗?" 话虽如此·,但是兄die, ...

  8. mysql事务手写笔记_兴奋了!阿里技术官手写“MySQL笔记”,传授你年薪百万级干货...

    前言 MySQL重要吗?重要!为什么重要?因为它在Java企业级开发中非常常用,因为 MySQL 是开源免费的,并且方便扩展.阿里巴巴数据库系统也大量用到了 MySQL,因此它的稳定性是有保障的.My ...

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

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

最新文章

  1. AI打败外科医生:机器人做手术,你敢来一刀吗?
  2. leetcode 636. Exclusive Time of Functions | 636. 函数的独占时间(Stack)
  3. Linux下CMAKE编译jsoncpp,使用CMake引入jsoncpp
  4. ar 和 nm 用法
  5. 计算机的复数英语怎么读,英语的复数怎么读
  6. linux for循环原理,linux for循环
  7. hashmap 线程不安全
  8. 易语言在线播放器源码php,易语言视频播放器源代码
  9. round在oracle里怎么用,Oracle round 函数(图)
  10. 抖音怎么用计算机数字等于中文字,抖音数字表白公式怎么玩 抖音数字表白从1到100攻略...
  11. Caused by: java.lang.UnsatisfiedLinkError: No implementation found for void com.geoway.mobile.utils
  12. kaggle上面的E-Commerce Data数据集练习(可视化与部分特征工程)
  13. 5项基因改造让你拥抱大财富
  14. 2021年中国给水泵市场趋势报告、技术动态创新及2027年市场预测
  15. MySQL增加新的分区
  16. 数字图像处理吴娱课后答案_何东健数字图像处理课后答案
  17. 【基础】计算机网络相关的内容
  18. 第三章 模块定义图:模块
  19. 恭喜,拓网科技在51CTO博客安家啦!!!
  20. ArchLinux + DDE 安装教程

热门文章

  1. 基于微信的旅游小程序、景区景点购票小程序、毕业设计、开题报告、毕业论文参考(2)Java管理后台、springboot框架
  2. C++20三路比较运算符
  3. Arduino循迹小车(三路红外)
  4. Windows取证——基本网络命令
  5. 连连直充1688跨境宝,如何在连连绑定跨境宝账号?
  6. Java获取当前时间前10分钟的时间
  7. 电子版论文怎么弄打印,哪里打印论文便宜
  8. 独立站常用建站工具大盘点——附收款方式及优惠
  9. 使用Windows7旗舰版64位来搭建本地HTTPS测试的ASP服务器
  10. MeeGo 1.2的repo使用方式