你对java工程师的理解_JAVA工程师常见面试题(一):谈谈对死锁的看法?
什么是死锁?
所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。 因此我们举个例子来描述,如果此时有一个线程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:Java、微服务、架构常见面试题(持续更新中)
Java.微服务.架构常见面试题(持续更新中) 文章目录 Java.微服务.架构常见面试题(持续更新中) ==**Java**== 1.Java概述 (1)JVM.JRE和JDK (2)Java特点 ...
- java常见手写sql面试题_java sql常见面试题
为管理学员培训信息,建立3个表: S (S#,SN,SD,SA) S#,SN,SD,SA 分别代表学号.学员姓名.所属单位.学员年龄 C (C#,CN ) C#,CN 分别代表课程编号.课程名称 SC ...
- BTA 常问的 Java基础40道常见面试题及详细答案
最近看到网上流传着,各种面试经验及面试题,往往都是一大堆技术题目贴上去,而没有答案. 为此我业余时间整理了,Java基础常见的40道常见面试题,及详细答案,望各路大牛,发现不对的地方,不吝赐教,留言即 ...
- 【JAVA秒会技术之秒杀面试官】JavaSE常见面试题(四)
[前言]别人都在你看不到的地方暗自努力,在你看得到的地方,他们也和你一样显得游手好闲,和你一样会抱怨,而只有你自己相信这些都是真的,最后,也只有你一个人继续不思进取 -- [下载]本人刚学习Jav ...
- Java后端工程师常见面试题
以下是整理的Java后端工程师常见面试题,希望有助于找工作: 1,对Java集合框架的理解.ArrayList和LinkedList的区别和优缺点,以及使用场景.扩容因子了解吗?分别是多少. Java ...
- 「高级java工程师」常见面试题及其答案(持续更新)
「java工程师」常见面试题及其答案请见: 「java工程师」常见面试题及其答案(持续更新)_好人老李的博客-CSDN博客 目录 java基础 常用的 jvm 调优方法? OOM的常见场景及其原因.解 ...
- 「java工程师」常见面试题及其答案(持续更新)
「高级java工程师」常见面试题及其答案: 「高级java工程师」常见面试题及其答案(持续更新)_好人老李的博客-CSDN博客 目录 java基础 面向对象与面向过程的区别? JRE.JDK.JVM的 ...
- Java工程师常见面试题集锦
Java工程师常见面试题集锦(一)互联网人必看!(附答案及视频教程,持续更新) 2019年01月02日 14:01:14 CSDNedu 阅读数:653 大牛也怕面试题,尤其是基础题,在面试中如果出现 ...
- 测试开发工程师常见面试题
1. CPU 和 GPU的区别 CPU 力气大啥P事都能干,还要协调. GPU 上面那家伙的小弟,老大让他处理图形,这方面处理简单,但是量大,老大虽然能处理,可是老大只有那么几个兄弟,所以不如交给小弟 ...
最新文章
- Android listview viewholder
- mysql 5.6.23 源码包安装报错_大环境下MySQL5.6源码安装实战一步步教你 CentOS6.5_64bit下编译安装...
- 压缩图片_Word快速压缩图片大小
- Gearman + Nodejs + MySQL UDF异步实现 MySQL 到 Redis 的数据同步
- Oracle Data Integrator之代理创建
- 利用 QQWry.Dat 实现 IP 地址高效检索(PHP)
- python生成序列_python如何生成随机序列?
- MITK通用插件(四)The Dicom Browser Plugin
- 论文查重系统哪个好?
- jvm学习——jvm内存区域
- 圆形头像制作,仿QQ做法。
- PHP开发API签名验证
- 瞬变抑制二极管TVS原理简介
- android xp 支付宝自动提现,支付宝商户如何自动提现?
- Security登录页面显示:Bad credentials 或者 BCryptPasswordEncoder : Encoded password does not look like BCrypt
- 项目管理-8-径回东土 五圣成真(大结局)
- SSM+校园社团平台 毕业设计-附源码251554
- 怎样在ZBrush中快速绘制人体躯干
- JS事件监听手机屏幕触摸事件 Touch
- 分享一个查询快递物流单号的方法