谈谈java中的死锁

更新时间:2023-06-30 09:10:06 阅读: 评论:0

谈谈java中的死锁
什么是死锁
在使⽤多线程以及多进程时,两个或两个以上的运算单元(进程、线程或协程),各⾃占有⼀些共享资源,并且互相等待其他线程占有的资源才能进⾏,⽽导致两个或者多个线程都在等待对⽅释放资源,就称为死锁
下⾯看个简单的例⼦:
public class DeadLockTest {
public static void main(String[] args) {
Object lock1 = new Object(); // 锁1
Object lock2 = new Object(); // 锁2
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
// 先获取锁1
synchronized (lock1) {
System.out.println("Thread 1:获取到锁1!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//获取锁2
System.out.println("Thread 1:等待获取2...");
exact time
synchronized (lock2) {
System.out.println("Thread 1:获取到锁2!");
}
}
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
// 先获取锁2
交通工具的英语单词
synchronized (lock2) {
System.out.println("Thread 2:获取到锁2!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取锁1
System.out.println("Thread 2:等待获取1...");
synchronized (lock1) {
System.out.println("Thread 2:获取到锁1!");
good wife
}
}
}
});
t2.start();
}
}
死锁产⽣原因
死锁只有同时满⾜以下四个条件才会发⽣:
互斥条件:线程对所分配到的资源具有排它性,在某个时间内锁资源只能被⼀个线程占⽤。如下图:
线程1已经持有的资源,不能再同时被线程2持有,如果线程2请求获取线程1已经占⽤的资源,那线程2只能等待,直到线程1释放资源。如下图:bottle什么意思
持有并请求条件:
持有并请求条件是指,当线程1已经持有了资源1,⼜想申请资源2,⽽资源2已经被线程3持有了,所以线程1就会处于等待状态,但是线程1在等待资源2的同时并不会释放已经持有的资源1。
不可剥夺条件:当线程已经持有了资源,在未使⽤完之前,不能被剥夺。如下图:
线程2如果也想使⽤此资源,只能等待线程1使⽤完并释放后才能获取。
环路等待条件:在死锁发⽣的时候,两个线程获取资源的顺序构成了环形链,如下图:
线程1已经持有资源2,想获取资源1,线程2已经获取了资源1,想请求资源2,这就形成资源请求等待的环形图。死锁的排查
1.jstack
⽤法如下:
/opt/java8/bin/jstack
Usage:
jstack [-l] <pid>
(to connect to running process) 连接活动线程
jstack -F [-m] [-l] <pid>
(to connect to a hung process) 连接阻塞线程
jstack [-m] [-l] <executable> <core>
(to connect to a core file) 连接dump的⽂件籍贯英文
jstack [-m] [-l] [rver_id@]<remote rver IP or hostname>
(to connect to a remote debug rver) 连接远程服务器
Options:
-F  to force a thread dump. U when jstack <pid> does not respond (process is hung)
-m  to print both java and native frames (mixed mode)
-
l  long listing. Prints additional information about locks
-h or -help to print this help message
死锁⽇志如下:
"mythread2" #12 prio=5 os_prio=0 tid=0x0000000058ef7800 nid=0x1ab4 waiting on condition [0x0000000059f8f000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for  <0x00000000d602d610> (a urrent.lock
s.ReentrantLock$NonfairSync)
at urrent.locks.LockSupport.park(LockSupport.java:175)
at urrent.locks.AbstractQueuedSynchronizer.parkAndCheckInt
errupt(AbstractQueuedSynchronizer.java:836)
at urrent.locks.AbstractQueuedSynchronizer.acquireQueued(A
bstractQueuedSynchronizer.java:870)anx
at urrent.locks.AbstractQueuedSynchronizer.acquire(Abstrac
tQueuedSynchronizer.java:1199)
at urrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLo
ck.java:209)
at urrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
at DeathLock$2.run(DeathLock.java:34)
Locked ownable synchronizers:
- <0x00000000d602d640> (a urrent.locks.ReentrantLock$Nonfa
irSync)
"mythread1" #11 prio=5 os_prio=0 tid=0x0000000058ef7000 nid=0x3e68 waiting on condition [0x000000005947f000]
服务业发票java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for  <0x00000000d602d640> (a urrent.lock
s.ReentrantLock$NonfairSync)
at urrent.locks.LockSupport.park(LockSupport.java:175)
at urrent.locks.AbstractQueuedSynchronizer.parkAndCheckInt
errupt(AbstractQueuedSynchronizer.java:836)
at urrent.locks.AbstractQueuedSynchronizer.acquireQueued(A
bstractQueuedSynchronizer.java:870)
at urrent.locks.AbstractQueuedSynchronizer.acquire(Abstrac
tQueuedSynchronizer.java:1199)
at urrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLo
ck.java:209)
at urrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
at DeathLock$1.run(DeathLock.java:22)
Locked ownable synchronizers:
- <0x00000000d602d610> (a urrent.locks.ReentrantLock$Nonfa
irSync)
Found one Java-level deadlock:
=============================
"mythread2":
waiting for ownable synchronizer 0x00000000d602d610, (a urrent.l
ocks.ReentrantLock$NonfairSync),
which is held by "mythread1"
"mythread1":
waiting for ownable synchronizer 0x00000000d602d640, (a urrent.l
ocks.ReentrantLock$NonfairSync),
which is held by "mythread2"
Java stack information for the threads listed above:
===================================================
"mythread2":
at sun.misc.Unsafe.park(Native Method)
-
parking to wait for  <0x00000000d602d610> (a urrent.lock
s.ReentrantLock$NonfairSync)
at urrent.locks.LockSupport.park(LockSupport.java:175)
at urrent.locks.AbstractQueuedSynchronizer.parkAndCheckInt
errupt(AbstractQueuedSynchronizer.java:836)
at urrent.locks.AbstractQueuedSynchronizer.acquireQueued(A
bstractQueuedSynchronizer.java:870)
at urrent.locks.AbstractQueuedSynchronizer.acquire(Abstrac
tQueuedSynchronizer.java:1199)
at urrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLo
ck.java:209)
at urrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
at DeathLock$2.run(DeathLock.java:34)
"mythread1":
at sun.misc.Unsafe.park(Native Method)
- parking to wait for  <0x00000000d602d640> (a urrent.lock
s.ReentrantLock$NonfairSync)
at urrent.locks.LockSupport.park(LockSupport.java:175)
at urrent.locks.AbstractQueuedSynchronizer.parkAndCheckInt
errupt(AbstractQueuedSynchronizer.java:836)
at urrent.locks.AbstractQueuedSynchronizer.acquireQueued(A
bstractQueuedSynchronizer.java:870)
at urrent.locks.AbstractQueuedSynchronizer.acquire(Abstrac
tQueuedSynchronizer.java:1199)
at urrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLo
ck.java:209)
at urrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
at DeathLock$1.run(DeathLock.java:22)
Found 1 deadlock.
可以根据⽇志输出来直接定位到具体的死锁代码。
2.jmc
jmc是Oracle Java Mission Control的缩写,是⼀个对Java程序进⾏管理、监控、概要分析和故障排查的⼯具套件。在JDK的bin⽬录中,同样是双击启动,如下图所⽰:外套英语
jmc打开如下:
需要选择死锁类,右键启动JMX控制台,然后就可以发现死锁和死锁具体信息。
常⽤⼯具还有jconsole,jvisualvm等不再⼀⼀介绍,感兴趣的可以⾃已查看⼀下。
避免死锁问题的发⽣
死锁分析
还要看死锁产⽣的4个条件,只要不满⾜条件就⽆法产⽣死锁了,互斥条件和不可剥夺条件是系统特性⽆法阻⽌。只能通过破坏请求和保持条件或者是环路等待条件,从⽽来解决死锁的问题。
避免死锁的⽅案hardlywhen
⼀、固定加锁顺序
即通过有顺序的获取锁,从⽽避免产⽣环路等待条件,从⽽解决死锁问题的。来看环路等待的例⼦:entitle
线程1先获取了锁A,再请求获取锁B,线程2与线程1同时执⾏,线程2先获取锁B,再请求获取锁A,两个线程都先占⽤了各⾃的资源(锁A
和锁B)之后,再尝试获取对⽅的锁,从⽽造成了环路等待问题,最后造成了死锁。
此时只需将线程1和线程2获取锁的顺序进⾏统⼀,也就是线程1和线程2同时执⾏之后,都先获取锁A,再获取锁B。因为只有⼀个线程能成功获取到锁A,没有获取到锁A的线程就会等待先获取锁A,此时得到锁A的线程继续获取锁 B,因为没有线程争抢和拥有锁B,那么得到锁A 的线程就会顺利的拥有锁B,之后执⾏相应的代码再将锁资源全部释放,然后另⼀个等待获取锁A的线程就可以成功获取到锁资源,这样就不会出现死锁的问题了。
⼆、开放调⽤避免死锁
在协作对象之间发⽣死锁的场景中,主要是因为在调⽤某个⽅法时就需要持有锁,并且在⽅法内部也调⽤了其他带锁的⽅法,如果在调⽤某个⽅法时不需要持有锁,那么这种调⽤被称为开放调⽤,同步代码块最好仅被⽤于保护那些涉及共享状态的操作。
三、使⽤定时锁
使⽤显式Lock锁,在获取锁时使⽤tryLock()⽅法。当等待超过时限的时候,tryLock()不会⼀直等待,⽽是返回错误信息,能够有效避免死锁问题。
总结
简单来说,死锁问题的产⽣是由两个或者以上线程并⾏执⾏的时候,争夺资源⽽互相等待造成的。
死锁只有同时满⾜互斥、持有并等待、不可剥夺、环路等待这四个条件的时候才会发⽣。
所以要避免死锁问题,就是要破坏其中⼀个条件即可,最常⽤的⽅法就是使⽤资源有序分配法来破坏环路等待条件。

本文发布于:2023-06-30 09:10:06,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/78/1069915.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:线程   死锁   资源   等待   获取
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图