什么是死锁?

所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。 因此我们举个例子来描述,如果此时有一个线程A,按照先锁1再获得锁2的的顺序获得锁,而在此同时又有另外一个线程B,按照先锁2再锁1的顺序获得锁。如下图所示:

lock.png

我们可以在JAVA代码中模拟一下死锁的情况:

/**

* @author brianxia

* @version 1.0

* @date 2020/12/12 18:18

*/

public class DeadLock {

public static void main(String[] args) {

//创建两个资源文件用于加锁

Object obj1 = new Object();

Object obj2 = new Object();

new Thread(() -> {

//第一个线程,先拿到obj1,对obj1加锁

synchronized (obj1) {

try {

//休眠的作用是防止连续拿到两个资源

Thread.sleep(1000L);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized (obj2) {

}

}

}).start();

new Thread(() -> {

//第二个线程,先拿到obj2,对obj1加锁

synchronized (obj2) {

try {

//休眠的作用是防止连续拿到两个资源

Thread.sleep(1000L);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized (obj1) {

}

}

}).start();

}

}

上述代码模拟了最开始说明的情况:

线程A对obj1加锁

线程B对obj2加锁

线程A想要获取obj2的锁

线程B想要获取obj1的锁

双方互不让步,最终导致了死锁。

检测死锁

在JAVA中,可以使用jps+jstack命令检测死锁:

首先使用jps找到对应的进程ID,比如这个案例中,我的进程名字和类名是一致的DeadLock。

>jps

20512 QuorumPeerMain

10820 jar

15076 RemoteMavenServer36

22244 RemoteMavenServer36

14056 RemoteMavenServer36

21032 RemoteMavenServer36

26504

21036 RemoteMavenServer36

33712 DeadLock

34068 RemoteMavenServer36

21276 Launcher

30652 Jps

7196

接下来使用jstack分析线程运行情况:

jstack 33712

2020-12-12 18:21:06

Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.181-b13 mixed mode):

"DestroyJavaVM" #14 prio=5 os_prio=0 tid=0x0000000002bbe800 nid=0x8e88 waiting on condition [0x0000000000000000]

java.lang.Thread.State: RUNNABLE

"Thread-1" #13 prio=5 os_prio=0 tid=0x000000001fc31800 nid=0x24a4 waiting for monitor entry [0x00000000205be000]

java.lang.Thread.State: BLOCKED (on object monitor)

at DeadLock.lambda$main$1(DeadLock.java:35)

- waiting to lock <0x000000076c112c80> (a java.lang.Object)

- locked <0x000000076c112c90> (a java.lang.Object)

at DeadLock$$Lambda$2/1096979270.run(Unknown Source)

at java.lang.Thread.run(Thread.java:748)

"Thread-0" #12 prio=5 os_prio=0 tid=0x000000001fc2f800 nid=0x81b4 waiting for monitor entry [0x00000000204bf000]

java.lang.Thread.State: BLOCKED (on object monitor)

at DeadLock.lambda$main$0(DeadLock.java:21)

- waiting to lock <0x000000076c112c90> (a java.lang.Object)

- locked <0x000000076c112c80> (a java.lang.Object)

at DeadLock$$Lambda$1/1324119927.run(Unknown Source)

at java.lang.Thread.run(Thread.java:748)

"Service Thread" #11 daemon prio=9 os_prio=0 tid=0x000000001e061000 nid=0x5ad4 runnable [0x0000000000000000]

java.lang.Thread.State: RUNNABLE

"C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x000000001dfc8000 nid=0x8fd8 waiting on condition [0x0000000000000000]

java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000000001dfba800 nid=0x3cc4 waiting on condition [0x0000000000000000]

java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000000001dfb9800 nid=0x21bc waiting on condition [0x0000000000000000]

java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000000001dfa3000 nid=0x4834 waiting on condition [0x0000000000000000]

java.lang.Thread.State: RUNNABLE

"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000000001dfa6800 nid=0x6480 runnable [0x000000001f5be000]

java.lang.Thread.State: RUNNABLE

at java.net.SocketInputStream.socketRead0(Native Method)

at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)

at java.net.SocketInputStream.read(SocketInputStream.java:171)

at java.net.SocketInputStream.read(SocketInputStream.java:141)

at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)

at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)

at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)

- locked <0x000000076c24a6d8> (a java.io.InputStreamReader)

at java.io.InputStreamReader.read(InputStreamReader.java:184)

at java.io.BufferedReader.fill(BufferedReader.java:161)

at java.io.BufferedReader.readLine(BufferedReader.java:324)

- locked <0x000000076c24a6d8> (a java.io.InputStreamReader)

at java.io.BufferedReader.readLine(BufferedReader.java:389)

at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:61)

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000000001df13000 nid=0x6698 waiting on condition [0x0000000000000000]

java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001df6a000 nid=0x872c runnable [0x0000000000000000]

java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001def3000 nid=0x3140 in Object.wait() [0x000000001f25f000]

java.lang.Thread.State: WAITING (on object monitor)

at java.lang.Object.wait(Native Method)

- waiting on <0x000000076bf88ed0> (a java.lang.ref.ReferenceQueue$Lock)

at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)

- locked <0x000000076bf88ed0> (a java.lang.ref.ReferenceQueue$Lock)

at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)

at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000000001c7fc800 nid=0x620 in Object.wait() [0x000000001f15e000]

java.lang.Thread.State: WAITING (on object monitor)

at java.lang.Object.wait(Native Method)

- waiting on <0x000000076bf86bf8> (a java.lang.ref.Reference$Lock)

at java.lang.Object.wait(Object.java:502)

at java.lang.ref.Reference.tryHandlePending(Reference.java:191)

- locked <0x000000076bf86bf8> (a java.lang.ref.Reference$Lock)

at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=2 tid=0x000000001c7f7000 nid=0x2500 runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002bd8800 nid=0x8f80 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000002bda000 nid=0x2a5c runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000002bdb800 nid=0x4b80 runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000002bdd000 nid=0x3b24 runnable

"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x0000000002be0800 nid=0x831c runnable

"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000002be1800 nid=0x4330 runnable

"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000000002be5000 nid=0x8654 runnable

"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000000002be6000 nid=0x824c runnable

"GC task thread#8 (ParallelGC)" os_prio=0 tid=0x0000000002be7000 nid=0x7214 runnable

"GC task thread#9 (ParallelGC)" os_prio=0 tid=0x0000000002be8800 nid=0x8474 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x000000001e074000 nid=0x32e8 waiting on condition

JNI global references: 316

Found one Java-level deadlock:

=============================

"Thread-1":

waiting to lock monitor 0x000000001c803628 (object 0x000000076c112c80, a java.lang.Object),

which is held by "Thread-0"

"Thread-0":

waiting to lock monitor 0x000000001c800d98 (object 0x000000076c112c90, a java.lang.Object),

which is held by "Thread-1"

Java stack information for the threads listed above:

===================================================

"Thread-1":

at DeadLock.lambda$main$1(DeadLock.java:35)

- waiting to lock <0x000000076c112c80> (a java.lang.Object)

- locked <0x000000076c112c90> (a java.lang.Object)

at DeadLock$$Lambda$2/1096979270.run(Unknown Source)

at java.lang.Thread.run(Thread.java:748)

"Thread-0":

at DeadLock.lambda$main$0(DeadLock.java:21)

- waiting to lock <0x000000076c112c90> (a java.lang.Object)

- locked <0x000000076c112c80> (a java.lang.Object)

at DeadLock$$Lambda$1/1324119927.run(Unknown Source)

at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

重点来看最后一段话:

image.png

jstack命令已经发现了一个死锁,同时在下方标注了锁的ID。

Thread-0拥有0x000000076c112c80锁,等待0x000000076c112c90锁。

Thread-1拥有0x000000076c112c90锁,等待0x000000076c112c80锁。

这样就发生了死锁。

死锁产生的4个必要条件?

互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。

案例中线程1一直持有obj1的锁,线程2一直持有obj2的锁

请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。

线程均不释放锁

不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。

无法强制释放锁

环路等待条件:在发生死锁时,必然存在一个进程--资源的环形链。

解决死锁的基本方法

资源一次性分配:一次性分配所有资源,这样就不会再有请求了:(破坏请求条件)

只要有一个资源得不到分配,也不给这个进程分配其他的资源:(破坏请保持条件)

可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)

资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)

这里具体说一下资源有序分配法,这是原来的分配方式:

image.png

修改后的:

image.png

这样就不会产生死锁了。

/**

* @author brianxia

* @version 1.0

* @date 2020/12/12 18:18

*/

public class DeadLock {

public static void main(String[] args) {

//创建两个资源文件用于加锁

Object obj1 = new Object();

Object obj2 = new Object();

new Thread(() -> {

//第一个线程,先拿到obj1,对obj1加锁

synchronized (obj1) {

try {

//休眠的作用是防止连续拿到两个资源

Thread.sleep(1000L);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized (obj2) {

}

}

}).start();

new Thread(() -> {

//第二个线程,先拿到obj1,对obj2加锁,按照有序的方式解决死锁

synchronized (obj1) {

try {

//休眠的作用是防止连续拿到两个资源

Thread.sleep(1000L);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized (obj2) {

}

}

}).start();

}

}

当然还有一种方式是使用lock进行超时取消获取锁:

import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.ReentrantLock;

/**

* @author brianxia

* @version 1.0

* @date 2020/12/12 18:18

*/

public class DeadLock2 {

public static void main(String[] args) {

//创建两个资源文件用于加锁

ReentrantLock reentrantLock1 = new ReentrantLock();

ReentrantLock reentrantLock2 = new ReentrantLock();

new Thread(() -> {

reentrantLock1.lock();

try {

//休眠的作用是防止连续拿到两个资源

Thread.sleep(1000L);

} catch (InterruptedException e) {

e.printStackTrace();

}

try {

if (reentrantLock2.tryLock(200, TimeUnit.MILLISECONDS)) {

System.out.println("线程1拿到锁2");

}

} catch (InterruptedException e) {

e.printStackTrace();

}

reentrantLock1.unlock();

System.out.println("线程1解锁1");

}).start();

new Thread(() -> {

reentrantLock2.lock();

try {

//休眠的作用是防止连续拿到两个资源

Thread.sleep(1000L);

} catch (InterruptedException e) {

e.printStackTrace();

}

try {

if (reentrantLock1.tryLock(200, TimeUnit.MILLISECONDS)) {

System.out.println("线程2拿到锁1");

}

} catch (InterruptedException e) {

e.printStackTrace();

}

reentrantLock2.unlock();

System.out.println("线程2解锁2");

}).start();

}

}

你对java工程师的理解_JAVA工程师常见面试题(一):谈谈对死锁的看法?相关推荐

  1. 面试1:Java、微服务、架构常见面试题(持续更新中)

    Java.微服务.架构常见面试题(持续更新中) 文章目录 Java.微服务.架构常见面试题(持续更新中) ==**Java**== 1.Java概述 (1)JVM.JRE和JDK (2)Java特点 ...

  2. java常见手写sql面试题_java sql常见面试题

    为管理学员培训信息,建立3个表: S (S#,SN,SD,SA) S#,SN,SD,SA 分别代表学号.学员姓名.所属单位.学员年龄 C (C#,CN ) C#,CN 分别代表课程编号.课程名称 SC ...

  3. BTA 常问的 Java基础40道常见面试题及详细答案

    最近看到网上流传着,各种面试经验及面试题,往往都是一大堆技术题目贴上去,而没有答案. 为此我业余时间整理了,Java基础常见的40道常见面试题,及详细答案,望各路大牛,发现不对的地方,不吝赐教,留言即 ...

  4. 【JAVA秒会技术之秒杀面试官】JavaSE常见面试题(四)

    [前言]别人都在你看不到的地方暗自努力,在你看得到的地方,他们也和你一样显得游手好闲,和你一样会抱怨,而只有你自己相信这些都是真的,最后,也只有你一个人继续不思进取 --   [下载]本人刚学习Jav ...

  5. Java后端工程师常见面试题

    以下是整理的Java后端工程师常见面试题,希望有助于找工作: 1,对Java集合框架的理解.ArrayList和LinkedList的区别和优缺点,以及使用场景.扩容因子了解吗?分别是多少. Java ...

  6. 「高级java工程师」常见面试题及其答案(持续更新)

    「java工程师」常见面试题及其答案请见: 「java工程师」常见面试题及其答案(持续更新)_好人老李的博客-CSDN博客 目录 java基础 常用的 jvm 调优方法? OOM的常见场景及其原因.解 ...

  7. 「java工程师」常见面试题及其答案(持续更新)

    「高级java工程师」常见面试题及其答案: 「高级java工程师」常见面试题及其答案(持续更新)_好人老李的博客-CSDN博客 目录 java基础 面向对象与面向过程的区别? JRE.JDK.JVM的 ...

  8. Java工程师常见面试题集锦

    Java工程师常见面试题集锦(一)互联网人必看!(附答案及视频教程,持续更新) 2019年01月02日 14:01:14 CSDNedu 阅读数:653 大牛也怕面试题,尤其是基础题,在面试中如果出现 ...

  9. 测试开发工程师常见面试题

    1. CPU 和 GPU的区别 CPU 力气大啥P事都能干,还要协调. GPU 上面那家伙的小弟,老大让他处理图形,这方面处理简单,但是量大,老大虽然能处理,可是老大只有那么几个兄弟,所以不如交给小弟 ...

最新文章

  1. Android listview viewholder
  2. mysql 5.6.23 源码包安装报错_大环境下MySQL5.6源码安装实战一步步教你 CentOS6.5_64bit下编译安装...
  3. 压缩图片_Word快速压缩图片大小
  4. Gearman + Nodejs + MySQL UDF异步实现 MySQL 到 Redis 的数据同步
  5. Oracle Data Integrator之代理创建
  6. 利用 QQWry.Dat 实现 IP 地址高效检索(PHP)
  7. python生成序列_python如何生成随机序列?
  8. MITK通用插件(四)The Dicom Browser Plugin
  9. 论文查重系统哪个好?
  10. jvm学习——jvm内存区域
  11. 圆形头像制作,仿QQ做法。
  12. PHP开发API签名验证
  13. 瞬变抑制二极管TVS原理简介
  14. android xp 支付宝自动提现,支付宝商户如何自动提现?
  15. Security登录页面显示:Bad credentials 或者 BCryptPasswordEncoder : Encoded password does not look like BCrypt
  16. 项目管理-8-径回东土 五圣成真(大结局)
  17. SSM+校园社团平台 毕业设计-附源码251554
  18. 怎样在ZBrush中快速绘制人体躯干
  19. JS事件监听手机屏幕触摸事件 Touch
  20. 分享一个查询快递物流单号的方法

热门文章

  1. three.js学习笔记(四)——Lights灯光
  2. mysql权限系统的工作原理_MySQL
  3. MFC绘制有理柱面和圆锥面
  4. idea跳到下一个断点_IDEA---断点调试Debug
  5. 如何能成为一个更优秀的人
  6. C# CultureInfo中常用的InvariantCulture
  7. 周计划周记录:2022年6月13日-2022年6月19日
  8. 【狂神说Java】JavaWeb入门到实战--Maven详解
  9. 小米电视6和小米电视6至尊版区别
  10. Python3 模拟手机登录熊猫直播(panda.tv)