Java多线程编程之死锁
来源:互联网 发布:淘宝店 虎扑识货认证 编辑:程序博客网 时间:2024/06/01 12:34
同步造成的死锁问题是说两(多)个线程互相占用了对方所需要的资源,等待对方释放资源僵持导致程序无人工干预不能结束的情况。
类似于哲学家就餐问题,比如共有3个资源被3个线程访问,每个线程必须拿到2个资源才能保持正常运行。如果这3个线程在同一时刻取得了这3个资源,又没有线程愿意让出资源,这时候就会出现3个线程互相等待其它线程释放资源的情况而导致死锁的出现(或者A依赖于B,B依赖于C,C依赖于A,它们互相取得了对方的资源)。
这里参考一个已经写好的例子对死锁作分析。
Deadlock in Java Example
例:程序设计了一个SyncThread类,类中对对象申请了同步锁(即当对象资源被占用时其它线程不能取得该对象锁。如果暂时无线程取得该对象锁,在同一时间两个线程进入了该同步块中,则两个对象均获得了该锁),而且一个对象锁是在另一个对象锁里面锁定的,work()方法休眠3000ms即代表占用该对象资源3000ms。
新建三个Thread t1,t2,t3分步执行,初始化Runnable类SyncThread类的实例。t1,t2,t3分别申请(obj1,obj2),(obj2,obj3),(obj3,obj1),三个线程分别与其它线程有一个共享的资源。当一个线程获取了两个对象后会自动释放其获取的对象资源,如果一直获取不到两个对象资源就会僵持不下。
public class DeadLock { public static long begin; public static void main(String[] args) throws InterruptedException { begin = System.currentTimeMillis(); Object obj1 = new Object(); Object obj2 = new Object(); Object obj3 = new Object(); Thread t1 = new Thread(new SyncThread(obj1, obj2), "t1"); Thread t2 = new Thread(new SyncThread(obj2, obj3), "t2"); Thread t3 = new Thread(new SyncThread(obj3, obj1), "t3"); t1.start(); Thread.sleep(2000); t2.start(); Thread.sleep(2000); t3.start(); long end = System.currentTimeMillis() - begin; System.out.println("end = " + end); }}class SyncThread implements Runnable { private Object obj1; private Object obj2; public SyncThread(Object o1, Object o2) { this.obj1 = o1; this.obj2 = o2; } @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name + " acquiring lock on " + obj1); synchronized (obj1) { System.out.println(name + " acquired lock on " + obj1); work(); System.out.println(name + " acquiring lock on " + obj2); synchronized (obj2) { System.out.println(name + " acquired lock on " + obj2); work(); } System.out.println(name + " released lock on " + obj2); } System.out.println(name + " released lock on " + obj1); System.out.println(name + " finished execution."); } private void work() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } }}
查看此程序的结果
t1 acquiring lock on java.lang.Object@61a0353d
t1 acquired lock on java.lang.Object@61a0353d
t2 acquiring lock on java.lang.Object@11b75be2
t2 acquired lock on java.lang.Object@11b75be2
t1 acquiring lock on java.lang.Object@11b75be2
end = 4032
t3 acquiring lock on java.lang.Object@3219ab8d
t3 acquired lock on java.lang.Object@3219ab8d
t2 acquiring lock on java.lang.Object@3219ab8d
t3 acquiring lock on java.lang.Object@61a0353d
可以看出t1、t2、t3一直处于申请资源状态的死锁中,那么为什么会出现这种状态呢?可以分析下线程申请对象资源的时间轴:
在0s时候,t1获取obj1对象;
休眠2s后,即第2s,t2获得了obj2对象;
当t1在执行work方法休眠3s后,即第3s,t1想要去获取obj2对象,此时发现obj2对象已经被t2占用了,此时t1被阻塞;
在第2s后休眠的2s后,即第4s,t3获取了obj3对象;
第2s后的3s后(t2执行完work方法),即第5s,t2想要去获取obj3对象,此时obj3对象已经被t3占用了,此时t2被阻塞;
在t3获取obj3对象并执行完work的时刻,即第7s,t3想要去获取obj1对象,此时obj1对象正在阻塞中,被t1占用,此时t3因为不能获取资源而陷入等待阻塞中。
这个时候t1,t2,t3互相占用了对方需要的资源,从此陷入了无尽的等待中。
这个死锁看起来是时间卡得比较紧,那么如果将主函数中的
t2.start();Thread.sleep(2000);
的休眠时间改成3000ms,即更改线程之间的开始运行时间
t2.start();Thread.sleep(3000);
又会出现什么样的效果呢?
结果如下:
t1 acquiring lock on java.lang.Object@5d0769dd
t1 acquired lock on java.lang.Object@5d0769dd
t2 acquiring lock on java.lang.Object@1cf15b84
t2 acquired lock on java.lang.Object@1cf15b84
t1 acquiring lock on java.lang.Object@1cf15b84
t2 acquiring lock on java.lang.Object@334dcfad
end = 5016
t2 acquired lock on java.lang.Object@334dcfad
t3 acquiring lock on java.lang.Object@334dcfad
t3 acquired lock on java.lang.Object@334dcfad
t2 released lock on java.lang.Object@334dcfad
t2 released lock on java.lang.Object@1cf15b84
t2 finished execution.
t1 acquired lock on java.lang.Object@1cf15b84
t3 acquiring lock on java.lang.Object@5d0769dd
t1 released lock on java.lang.Object@1cf15b84
t3 acquired lock on java.lang.Object@5d0769dd
t1 released lock on java.lang.Object@5d0769dd
t1 finished execution.
t3 released lock on java.lang.Object@5d0769dd
t3 released lock on java.lang.Object@334dcfad
t3 finished execution.
申请资源的时间轴如下:
分析和上面的类似,由于给了线程之间足够多的时间过度去获得资源和利用资源,可以使得线程能够及时运行完并释放对象,从而避免了死锁的发生。
但是实际开发过程中我们很难预测到它们花费的具体时间,无法精确地通过它们的间隔来避免死锁的发生,但是可以通过其它的方法来避免死锁的发生。第一就是可以设置一个最长的等待时间,超过这个等待时间就释放资源而不是一直占用着不放,这个有点类似于时间片的功能;第二就是尽量不要互相占用对方的资源(当然这个也得根据需求来设计);再者就是尽量不要把整个对象全给锁住,可以锁住更小的区域,可以考虑使用Lock实现。
- Java多线程编程之死锁
- java多线程之死锁
- java多线程之死锁
- java多线程之死锁
- java多线程之--死锁
- Java---多线程之死锁
- java多线程之死锁
- Java 多线程之死锁
- Java多线程之死锁
- Java多线程之死锁
- java多线程之死锁
- java多线程之死锁
- java多线程之死锁
- java多线程之-死锁
- Java 多线程编程之七:死锁(附源代码)
- Java 多线程编程之七:死锁(附源代码)
- Java 多线程编程之七:死锁(附源代码)
- java多线程之模拟死锁
- 大数据量下高并发同步
- 实例009 求n!
- 五、恢复到v1.0版本
- UVA10801
- C++实验5-数组分离
- Java多线程编程之死锁
- 努力成为一个有本事,没脾气的程序员
- SpringAOP详细介绍
- Sublime Text 3 安装及常用插件配置
- HTM基础知识(三)
- 中南大学第十一届大学生程序设计竞赛网络预选赛总结
- 输入3个整数x,y,z按从小到大的顺序输出
- 用Servlet写一个jsp页面
- Set、List、Map