何为死锁
两个线程相互等待对方已锁定的资源,产生死锁
如何避免死锁
其实这个问题很难回答,因为至少目前在Java领域,在语言层面是无法避免死锁的。更多的是依赖编程经验。
模拟死锁
比如有两个线程,共享一个对象。该对象持有 A、B 两份资源。线程1负责读取,线程2负责写入。
读取线程先占用A的锁,然后准备获取B的锁;写入线程先占用B的锁,再获取A的锁。
这个过程非常简单,但是一旦进入某个临界点,即读取线程等待写入线程占有的B锁。
写入线程等待读取线程占有的A锁,那么就很容易就产生了死锁。看如下代码演示。
/**
* DeadLockDemo.java
* com.yli.thread.demo
*/
package com.yli.thread.demo;
/**
* 死锁模拟
*
* @author yli
* @since Ver 1.1
* @Date 2016 2016年1月3日 下午8:29:01
*/
public class DeadLockDemo {
public static void main(String[] args) {
// 共享对象
DeadLock deadObj = new DeadLock();
// 读取线程
DeadLockRead threadRead = new DeadLockRead(deadObj);
// 写入线程
DeadLockWrite threadWrite = new DeadLockWrite(deadObj);
// 启动读取线程
threadRead.start();
// 启动写入线程
threadWrite.start();
}
// 读取线程class
private static class DeadLockRead extends Thread {
private DeadLock dead;
public DeadLockRead(DeadLock deadObj) {
dead = deadObj;
}
@Override
public void run() {
dead.read();
}
}
// 写入线程class
private static class DeadLockWrite extends Thread {
private DeadLock dead;
public DeadLockWrite(DeadLock deadObj) {
dead = deadObj;
}
@Override
public void run() {
dead.write();
}
}
}
// 共享对象提供read和write方法
class DeadLock {
private Object objA = new Object();
private Object objB = new Object();
public void read() {
String currentThread = Thread.currentThread().getName();
// 先让读取线程获取objA的锁
synchronized (objA) {
System.out.println(currentThread + " 获得了Obj-A的锁");
try {
System.out.println(currentThread + " 休眠10s,让其他线程先获得Obj-B的锁!");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 视图获得obj-B对象的锁,但write线程已经获得该对象的锁!
synchronized (objB) {
System.out.println(currentThread + " 获得了Obj-B的锁");
}
}
}
public void write() {
String currentThread = Thread.currentThread().getName();
try {
// 写入线程先休眠300毫米,确保读取线程已经运行起来,且获得objA的锁
Thread.sleep(300);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
// 由于读取线程获得objA的锁之后,会休眠10秒,所以写入线程有充分时间先获得objB的锁
synchronized (objB) {
System.out.println(currentThread + " 获得了Obj-B的锁");
// 视图获得obj-A的锁,但read线程已经获得该对象上的锁
synchronized (objA) {
System.out.println(currentThread + " 获得了Obj-A的锁");
}
}
}
}
检测死锁
之前介绍,在语言层面是无法阻止产生死锁的,但还好有工具为我们检测死锁提供便利,介绍两种常见方法。
方法1 jconsole 检测死锁
使用JDK bin目录下的jconsole工具,先运行上述demo,再打开该工具
方法2 jstack 检测死锁
jconsole提供可视化窗体来检测死锁,也是JDK官方提供的非常强大的JVM故障定位工具。
但对于很多运行在linux上的应用来说,如果在未授权的情况下,通过运行在windows上的jconsole来远程连接运行在linux的JVM,可能不是很方便,特别是在很多大公司,特别是线上环境!!!你几乎没有任何机会来使用这种工具,主要是担心我们不专业,把线上直接搞崩溃。。。
所以,jstack 命令行工具就可以派上用场了,它也是JDK bin目录下的一个工具,非常强大
如果在windows环境,cmd进入命令行窗口
step1-获取JVM进程号
如果你在linux上运行Java应用程序,也可以直接使用jsp命令,如果不允许使用该命令,那么这样
ps -ef|grep java 获取所有JVM的进程号,找到你想要的即可
step2-打印当前JVM堆栈
如果在linux上,也可以直接运行该命令,如果不允许使用。。。那就没辙了,找相应管理员给你授权。
step3-发现死锁信息
在step2结束之后,你就拿到JVM当前堆栈信息了,在使用 jstack 命令时,因为带上了 -l 选项,所以会打印出死锁信息,如下所示。
如果对JVM常用工具不熟悉,可以参考:http://blog.csdn.net/lojze_ly/article/details/49498981