您的位置:首页 > 编程语言 > Java开发

线程死锁

2016-01-03 21:01 357 查看

何为死锁

两个线程相互等待对方已锁定的资源,产生死锁

如何避免死锁

其实这个问题很难回答,因为至少目前在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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息