死锁定义
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
竞争的资源可以是:锁
、网络连接
、通知事件
,磁盘
、带宽
,以及一切可以被称作“资源”的东西。
死锁出现原因
- 因为系统资源不足导致的资源竞争
- 进程运行推进顺序不合适:请求和释放资源顺序不当
- 系统资源分配不当
出现死锁四个必要条件(这也是怎么打破这个死锁的入手)
资源互斥
:一个资源只能被一个进程使用请求与保持
:当一个进程因请求资源而阻塞的时候,保持已获得资源不放不剥夺
:进程已获得资源,在未使用完成之前,不能被其他进程强行剥夺循环等待
:若干进程之间形成一种头尾相接的循环等待资源关系
死锁产生场景
互相持有对方想获得的锁
如果此时有一个线程A,按照先锁a再获得锁b的的顺序获得锁,而在此同时又有另外一个线程B,按照先锁b再锁a的顺序获得锁。
public static void main(String[] args) {
final Object a = new Object();
final Object b = new Object();
Thread threadA = new Thread(new Runnable() {
public void run() {
synchronized (a) {
try {
System.out.println("now i in threadA-locka");
Thread.sleep(1000l);
synchronized (b) {
System.out.println("now i in threadA-lockb");
}
} catch (Exception e) {
// ignore
}
}
}
});
Thread threadB = new Thread(new Runnable() {
public void run() {
synchronized (b) {
try {
System.out.println("now i in threadB-lockb");
Thread.sleep(1000l);
synchronized (a) {
System.out.println("now i in threadB-locka");
}
} catch (Exception e) {
// ignore
}
}
}
});
threadA.start();
threadB.start();
}
线程池中的死锁
比如线程池中有一个线程可用,此时,任务A1依赖于任务2,任务1正在执行,但是任务2永源无法执行,因此造成死锁
如何解决(预防)死锁
死锁检测
Jstack命令
jstack是java虚拟机自带的一种堆栈跟踪工具。jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息。
Jstack工具可以用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。
Jconsole工具
Jconsole是JDK自带的监控工具,在JDK/bin目录下可以找到。它用于连接正在运行的本地或者远程的JVM,对运行在Java应用程序的资源消耗和性能进行监控,并画出大量的图表,提供强大的可视化界面。而且本身占用的服务器内存很小,甚至可以说几乎不消耗。
以确定的顺序获得锁
针对两个特定的锁,开发者可以尝试按照锁对象的hashCode值大小的顺序,分别获得两个锁,这样锁总是会以特定的顺序获得锁,那么死锁也不会发生。
超时放弃
当使用
synchronized
关键词提供的内置锁时,只要线程没有获得锁,那么就会永远等待下去,然而Lock
接口提供了boolean tryLock(long time, TimeUnit unit) throws InterruptedException
方法,该方法可以按照固定时长等待锁,因此线程可以在获取锁超时以后,主动释放之前已经获得的所有的锁。通过这种方式,也可以很有效地避免死锁。