先简单用生活列子介绍死锁,
2个人一起吃饭但是只有一双筷子,2人轮流吃(同时拥有2只筷子才能吃)。某一个时候,一个拿了左筷子,一人拿了右筷子,2个人都同时占用一个资源,等待另一个资源,这个时候甲在等待乙吃完并释放它占有的筷子,同理,乙也在等待甲吃完并释放它占有的筷子,这样就陷入了一个死循环,谁也无法继续吃饭。。
总而言之
当前线程不释放资源
当前线程拥有其他线程执行的资源
当前线程等待执行其他线程的资源
锁顺序死锁
-
线程A调用
method1()
方法,得到obj1锁 -
同时线程B调用
methd2()
方法,得到obj2锁 -
线程A和线程B都继续执行,此时线程A需要obj2锁才能继续往下执行。此时线程B需要obj1锁才能继续往下执行。
-
但是:线程A的obj1锁并没有释放,线程B的obj2锁也没有释放。
-
所以他们都只能等待,而这种等待是无期限的-->永久等待-->死锁
/**
*
*/
package demo3;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author liuchaojun
* @date 2018-12-14 上午11:02:50
*/
public class Lock1 {
public static void main(String[] args) {
Object obj1 = new Object();
Object obj2 = new Object();
ExecutorService e = Executors.newFixedThreadPool(5);
Task1 t = new Task1(obj1, obj2);
for (int i = 0; i < 10; i++) {
e.execute(t);
}
}
}
class Task1 implements Runnable{
private Object obj1;
private Object obj2;
/**
* @param obj1
* @param obj2
*/
public Task1(Object obj1, Object obj2) {
super();
this.obj1 = obj1;
this.obj2 = obj2;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
// 得到obj1锁
synchronized (obj1) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 得到obj2锁
synchronized (obj2) {
System.out.println("111111111");
}
}
// 得到obj2锁
synchronized (obj2) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 得到obj1锁
synchronized (obj1) {
System.out.println("2222222222222");
}
}
}
}
第二种:动态锁顺序死锁
-
线程A的用户accout转账给fromAccount
-
线程B的用户fromAccount转账给accout用户
-
如果两个线程同时进行调用method1方法
-
那么就会发生死锁。
/**
*
*/
package demo3;
/**
* @author liuchaojun
* @date 2018-12-14 上午11:02:50
*/
public class Lock2 {
void method1(Object account, Object fromAccount) {
synchronized (account) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 得到obj2锁
synchronized (fromAccount) {
System.out.println(account+"给"+fromAccount+"转账");
}
}
}
public static void main(String[] args) {
final Lock2 lock2 = new Lock2();
final Object obj1 = new Object();
final Object obj2 = new Object();
new Thread(new Runnable() {
@Override
public void run() {
lock2.aa(obj1, obj2);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
lock2.aa(obj2, obj1);
}
}).start();
}
}
二、避免死锁的方法
避免死锁可以概括成三种方法:
-
固定加锁的顺序(针对锁顺序死锁)
-
开放调用(针对对象之间协作造成的死锁)
-
使用定时锁-->
tryLock()
如果等待获取锁时间超时,则抛出异常而不是一直等待!
如上面转账代码,我们根据hash值进行上锁,固定加锁的顺序,这样就可以解决锁顺序死锁的问题
/**
*
*/
package demo3;
/**
* @author liuchaojun
* @date 2018-12-14 上午11:02:50
*/
public class Lock2 {
private static final Object newObj = new Object();
void method1(Object account, Object fromAccount) {
// 得到锁的hash值
int accountHash = System.identityHashCode(account);
int fromAccountHash = System.identityHashCode(fromAccount);
if (accountHash > fromAccountHash) {
synchronized (account) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 得到obj2锁
synchronized (fromAccount) {
System.out.println(account + "给" + fromAccount + "转账");
}
}
} else if (accountHash < fromAccountHash) {
synchronized (fromAccount) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 得到obj2锁
synchronized (account) {
System.out.println(account + "给" + fromAccount + "转账");
}
}
} else {
synchronized (newObj) {
synchronized (fromAccount) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 得到obj2锁
synchronized (account) {
System.out.println(account + "给" + fromAccount + "转账");
}
}
}
}
}
public static void main(String[] args) {
final Lock2 lock2 = new Lock2();
final Object obj1 = new Object();
final Object obj2 = new Object();
new Thread(new Runnable() {
@Override
public void run() {
lock2.method1(obj1, obj2);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
lock2.method1(obj2, obj1);
}
}).start();
}
}
使用定时锁
使用显式Lock锁,在获取锁时使用tryLock()
方法。当等待超过时限的时候,tryLock()
不会一直等待,而是返回错误信息。
使用tryLock()
能够有效避免死锁问题~~
2.4死锁检测
虽然造成死锁的原因是因为我们设计得不够好,但是可能写代码的时候不知道哪里发生了死锁。
JDK提供了两种方式来给我们检测:
-
JconsoleJDK自带的图形化界面工具,使用JDK给我们的的工具JConsole
-
Jstack是JDK自带的命令行工具,主要用于线程Dump分析。