死锁在类中只是有可能发生,并不是每次都发生,但是死锁是一个非常严重的一个问题,必须引起重视!
一、 什么是死锁?
概念
不同的线程分别占用对方的同步资源不放弃,都在等待对方放弃自己需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
补充
在数据库系统的设计中考虑了监测死锁以及从死锁中恢复,数据库如果监测到了一组事务发生了死锁时,将选择一个牺牲者并放弃这个事务。Java虚拟机解决死锁问题方面并没有数据库这么强大,当一组Java线程发生死锁时,这两个线程就永远不能再使用了,并且由于两个线程分别持有了两个锁,那么这两段同步代码/代码块也无法再运行了----除非终止并重启应用。
举个例子
当两个在一起吃饭的时候,只有一双筷子,此时只能使用着一双筷子来吃饭,你拿着这双筷子不放手,另外一个人也拿着不放手,都在等对方放手。此时的这种状态就形成了死锁。
三、 死锁的问题
处理线程同步时最容易出现。
下面是老师给的一个例子,运行起来就直接可以看到死锁的效果了(一直卡在运行界面)。
public class TestDeadLock {
/*
定义为static直接使用
*/
static StringBuffer sb1 = new StringBuffer();
static StringBuffer sb2 = new StringBuffer();
public static void main(String[] args) {
new Thread() {
public void run() {
synchronized (sb1) {
// sleep让死锁的效果更加明显
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
sb1.append("A");
synchronized (sb2) {
sb2.append("B");
System.out.println(sb1);
System.out.println(sb2);
}
}
}
}.start();
new Thread() {
public void run() {
synchronized (sb2) {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
sb1.append("C");
synchronized (sb1) {
sb2.append("D");
System.out.println(sb1);
System.out.println(sb2);
}
}
}
}.start();
}
}
解释一下这里出现死锁的原因:
程序进入第一个new Thread代码块中的run()方法之后,先握住sb1这把锁,执行到sleep,此时如果有其它线程也正在执行,就很有可能会被其它线程抢到CPU的执行权。这里有第二个线程(假设这个线程在10ms中抢到CPU的执行权的几率为100%),开始执行这里的代码块,当程序握住sb2这把锁的时候sleep一下。此时第一个线程又继续执行,发现此时也是sb2这把锁。于是出现了“你不让我,我不让你,都认为对方会放手” 的问题,也就是死锁。
二、 如何解决死锁?
1、 专门的算法、原则。
1) 让程序每次至多只能获得一个锁。当然,在多线程环境下,这种情况通常并不现实。
2) 设计时考虑清楚锁的顺序,尽量减少嵌在的加锁交互数量。
3) 既然死锁的产生是两个线程无限等待对方持有的锁,那么只要等待时间有个上限不就好了。
2、 尽量减少同步资源的定义