An example for dead lock:
package com.way.threads;
public class DeadLock {
public static void main(String[] args) throws InterruptedException{
Object obj1=new Object();
Object obj2=new Object();
Object obj3=new Object();
Thread t1=new Thread(new SyncThread(obj1,obj2),"thread1");
Thread t2=new Thread(new SyncThread(obj2,obj3),"thread2");
Thread t3=new Thread(new SyncThread(obj3,obj1),"thread3");
t1.start();
Thread.sleep(5000);
t2.start();
Thread.sleep(5000);
t3.start();
}
}
class SyncThread implements Runnable{
Object obj1;
Object obj2;
public SyncThread(Object obj1,Object obj2){
this.obj1=obj1;
this.obj2=obj2;
}
public void run(){
String name=Thread.currentThread().getName();
System.out.println(name+" begin to run");
synchronized(obj1){
System.out.println(name +" is using "+obj1);
System.out.println(name+" want "+obj2);
work();
synchronized (obj2) {
System.out.println(name +" is using "+obj2);
work();
}
System.out.println(name +" release "+obj2);
}
System.out.println(name +" release "+obj1);
System.out.println(name+" finish runing");
}
public void work(){
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Output:
thread1 begin to run
thread1 is using java.lang.Object@1aeb83ae
thread1 want java.lang.Object@142c6e83
thread2 begin to run
thread2 is using java.lang.Object@142c6e83
thread2 want java.lang.Object@22896964
thread3 begin to run
thread3 is using java.lang.Object@22896964
thread3 want java.lang.Object@1aeb83ae
Obviously, threads have been locked by each other. This is Dead Lock.
什么是死锁?
死锁的规范定义如下:如果一个进程集合中的每个进程都在等待只能由该进程集合中其他进程才能引发的事件,那么该进程集合就是死锁的。
产生死锁的原因主要是:
- 因为系统资源不足。
- 进程运行推进的顺序不合适。
- 资源分配不当等。
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
处理死锁:
四种处理死锁的策略:
- 鸵鸟策略(忽略死锁);
- 检测死锁并恢复;
- 仔细对资源进行分配,动态地避免死锁;
- 通过破坏引起死锁的四个必要条件之一,防止死锁的产生。
具体措施:
避免嵌套封锁:这是死锁最主要的原因的,如果你已经有一个资源了就要避免封锁另一个资源。如果你运行时只有一个对象封锁,那是几乎不可能出现一个死锁局面的。例如,这里是另一个运行中没有嵌套封锁的run()方法,而且程序运行没有死锁局面,运行得很成功。
尽可能减少封锁对象的数量和封锁时间:只对想要运行的资源获取封锁,比如封锁对象的特定属性而不是整个对象。减少封锁时间:如果一个线程要取得某一资源并封锁后,尽快完成这个资源相关的操作并及时释放。
调整资源占有顺序:类如最经典的哲学家吃饭问题,所有人都先拿左筷子后拿右筷子,如果其中一个反过来,思索概率就会降低。
设置最长等待:如果两个线程正在等待对象结束,无限期的使用线程加入,如果你的线程必须要等待另一个线程的结束,若是等待进程的结束加入最好准备最长时间。在java.util.concurrent库中有相关支持
“死锁”、“活锁”和“饥饿”:
死锁是多个线程发生资源的循环持有和等待导致程序不能进行的状态,定义明确。典型例子是哲学家吃饭问题。
活锁是指线程在持续运行,状态发生改变但没有产生成果的现象。活锁是可能自解的。典型例子是两车照面,都向同一个方向避让,继续照面。
饥饿是指多线程程序中的部分线程长时间不能得到执行,导致饿死。饥饿也是可以自解的。
避免方法:
死锁的定义和解决方法是明确的,解决死锁就是要打破死锁的必要条件:避免嵌套封锁;尽可能减少封锁对象的数量和封锁时间;调整资源占有顺序;设置最长等待;详见上一节。
避免活锁可以引入一些随机量,让一次“碰撞”之后第二次“碰撞概率”降低。
避免饥饿需要优化线程调度算法,最简单的方式使用先入先出(公平)调度器。
其他阅读资料:
http://www.importnew.com/9668.html
http://www.cnblogs.com/jijiji/p/4855581.html
http://blog.csdn.net/defonds/article/details/44021605/#t77
http://ifeve.com/java-concurrency-thread-directory/
《Java编程思想》