Java 多线程 死锁
多线程改善了系统资源的利用率并提高了系统 的处理能力,然而并发执行也带来了新的问题:死锁。所谓死锁是指多个线程在占有了对方所需的资源而又竞争对方的资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
通过一个实例来说明死锁现象。
//公共锁资源
class MyClock{
public static MyClock ClockA=new MyClock();
public static MyClock ClockB=new MyClock();
}
class Test implements Runnable{
private boolean flog;
Test(boolean flog){
this.flog=flog;
}
@Override
public void run() {
if (flog==true){
synchronized (MyClock.ClockA){//申请获得A资源并加锁
System.out.println(Thread.currentThread().getName()+"A1");
synchronized (MyClock.ClockB){申请获得B资源并加锁
System.out.println(Thread.currentThread().getName()+"B2");
}
}
}
else {
synchronized (MyClock.ClockB){//申请获得B资源并加锁
System.out.println(Thread.currentThread().getName()+"B3");
synchronized (MyClock.ClockA){//申请获得A资源并加锁
System.out.println(Thread.currentThread().getName()+"A4");
}
}
}
}
}
public class ThreadDem1 {
public static void main(String[] args) {
Test t1=new Test(true);
Test t2=new Test(false);
Thread th1=new Thread(t1);//创建线程
Thread th2=new Thread(t2);//创建线程
th1.start();//开启线程
th2.start();//开启线程
}
}
执行结果:
两个线程各自得到一个资源,又互相等待对方释放锁资源导致无法向前推进。
死锁产生的必要条件:
- 互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
- 不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。
- 请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
- 循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被链中下一个进程所请求。即存在一个处于等待状态的进程集合{Pl, P2, …, pn},其中Pi等 待的资源被P(i+1)占有(i=0, 1, …, n-1),Pn等待的资源被P0占有。
避免死锁的方式:
- 设计时考虑清楚锁的顺序,尽量减少嵌在的加锁交互数量。
- 让程序每次至多只能获得一个锁。当然,在多线程环境下,这种情况通常并不现实。
- 既然死锁的产生是两个线程无限等待对方持有的锁,那么只要等待时间有个上限不就好了。当然synchronized不具备这个功能,但是我们可以使用Lock类中的tryLock方法去尝试获取锁,这个方法可以指定一个超时时限,在等待超过该时限之后便会返回一个失败信息。