版权声明
- 本文原创作者:清风不渡
- 博客地址:https://blog.csdn.net/WXKKang
上面我们讲到了Java中线程互斥的机制,那就是给线程访问的数据上锁,那么这样就一定安全吗?答案肯定是否定的,这个世界上没有绝对安全的东西,而上篇所讲到的方法就会出现线程死锁的现象,那么这个现象是什么意思呢?我们该怎样处理或者说怎样避免发生这样的现象呢?下面我们就来一一学习吧
一、线程死锁
多线程中使用锁会造成效率低,如果出现了同步嵌套,就容易产生死锁问题
死锁是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象
如果两个线程之间有多个共享对象,如果两个线程分别持有对方所需要的共享对象的锁,在试图获取对方线程所持有的共享对象的锁时,就有死锁的危险
1、死锁产生的原因
加入我们现在有两个线程(线程1与线程2),这两个线程之间有两个共享对象(对象A和对象B),在线程执行的过程中,线程1与线程2都必须同时获得对象A与对象B后才能正常工作
那么当线程1获得了对象A的锁、线程2获得了对象B的锁,如果两个线程都不释放所获得的对象的锁,那么线程1将不能得到对象B的锁,线程2将不能得到对象A的锁,这种情况下两个线程都将被阻塞,即所谓的死锁
2、死锁的解决方案
死锁通常是由于资源的无序使用,导致线程不能得到正常执行所需要的资源
解决死锁问题的方法,就是所有线程使用一致的顺序申请资源锁。例如:
1、线程1和线程2都是按照对象A、对象B的顺序来获得对象锁
2、将对象A编号为1,对象B编号为2,线程1和线程2都必须先获得1号资源后方可再获取2号资源
3、死锁示例
下面我们来演示一个发生死锁的例子,这里有四个对象:艺术家、画匠、笔、纸。艺术家、画匠在工作时都需要同时有笔、纸 死锁演示效果:
1、艺术家首先得到笔,然后再去获取纸;工匠首先得到纸,然后再去获取笔
2、由于艺术家和工匠都不愿意放弃自己获得的笔、纸,因此二者都不能同时获得笔和纸,因此都无法工作
代码如下:
package qfbd.com;
/*
原创作者:清风不渡
博客地址:https://blog.csdn.net/WXKKang
*/
public class Demo {
public static void main(String[] args) throws Exception {
Resource resource = new Resource();
//创建艺术家对象
Artist artist = new Artist();
artist.prepace(resource);
//创建工匠
Limner limner = new Limner();
limner.prepace(resource);
//这里一定要先启动艺术家才能有死锁情况
artist.start();
limner.start();
}
}
//笔类
class Pen{
}
//纸类
class Paper{
}
//共享的资源类,这里同时有笔和纸
class Resource{
private Pen pen = new Pen();
private Paper paper = new Paper();
public Paper getPaper(){
return paper;
}
public Pen getPen(){
return pen;
}
}
/*
* 艺术家
*
* 这是一个线程类,在线程体中有两个同步块,分别是对pen和paper对象进行同步
*/
class Artist extends Thread {
private Resource resource;
@Override
public void run() {
Pen pen = resource.getPen();
System.out.println("艺术家获得笔!!!");
synchronized (pen) {
/*
* 加入睡眠原因:
* 1、在运行线程时,艺术家线程先执行,他首先获得笔
* 2、如果没有这个睡眠,那么可能画匠还没来的及获取到纸,
* 艺术家线程就获取到了纸,这样就无法测试死锁的情形了
*/
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("艺术家睡眠发生异常!!!");
}
//睡眠之后获取纸,但预测纸已经被画匠获取到了,此刻进入等待
Paper paper = resource.getPaper();
synchronized (paper) {
System.out.println("艺术家可以画画了!!");
}
}
}
public void prepace(Resource resource){
this.resource = resource;
}
}
/*
* 画匠
*
* 这是一个线程类,在线程体中有两个同步块,分别是对paper和pen对象进行同步
* 注意:他的同步对象paper和pen的顺序和艺术家是相反的
*/
class Limner extends Thread{
private Resource resource;
@Override
public void run() {
Paper paper = resource.getPaper();
System.out.println("画匠得到纸!!!");
synchronized (paper) {
//然后获取笔,预测被艺术家得到并睡眠,此刻进入等待
Pen pen = resource.getPen();
synchronized (pen) {
System.out.println("画匠可以画画了!!!");
}
}
}
public void prepace(Resource resource){
this.resource = resource;
}
}
执行结果如下:
可以发现,程序运行“卡死了”,即发生了所谓的死锁现象