死锁:是指两个或者两个以上的线程在执行过程中,因为争夺资源而造成的互相等待的现象,在无外力的情况下,这些线程会一直相互等待而无法继续运行下去(摘自《Java并发编程之美》)
死锁:某个任务在等待另一个任务,而后者又等待别的任务,这样一直下去,直到链条上的任务又在等待第一个任务释放锁,这得到了一个任务之间相互等待的连续循环,没有哪个线程能继续,被称之为死锁(摘自《Java编程思想》)
产生死锁的四个必备条件:
1、互斥条件:线程使用的资源中至少有一个不能共享
2、线程持有资源并等待另一个资源
3、资源不能被抢占:指线程获取到的资源在自己使用完之前不能被其他资源抢占,只有在自己使用完之后才有自己释放资源
4、循环等待资源,一直不退出
如何避免死锁?
要发生死锁的化,必须满足上面四个条件,所以 要防止死锁,破坏上述四个条件中的一个即可
经典死锁案例。哲学家用餐,哲学家围成一圈吃饭。哲学家很穷,买不起足够多的筷子,每个人左手边和右手边都有一只筷子,也就是多少哲学家就由多少只筷子,哲学家必须得到左手边和右手边的筷子才能就餐。如果哲学家左边和右边的人都在用筷子,则必须等待,直到有筷子可用
public class DieLock {
public static void main(String[] args) {
Philosopher[] philosophers=new Philosopher[20]; //人越少也容易死锁
ChopStick left,right,first;
left=new ChopStick();
right=new ChopStick();
first=left;
for(int i=0;i<philosophers.length-1;i++){
philosophers[i] =new Philosopher(left,right);//哲学家用餐放下筷子
left=right; //下一个哲学家用餐左手筷子,是已用完餐的哲学家右手的筷子。这个有点循环队列的感觉
right=new ChopStick();
}
//最后一个哲学家的右手ChopStick为first
philosophers[philosophers.length-1]=new Philosopher(left,first); //会死锁
// philosophers[philosophers.length-1]=new Philosopher(first,left); //不会死锁 ,破坏第4个条件
}
}
class ChopStick{
private static int counter=0;
private int number =counter++;
public String toString(){
return "ChopStick"+number;
}
}
class Philosopher extends Thread{
private static int counter=0;
private int number=counter++;
private ChopStick leftChopStick;
private ChopStick rightChopStick;
public Philosopher(ChopStick leftChopStick, ChopStick rightChopStick) {
this.leftChopStick = leftChopStick;
this.rightChopStick = rightChopStick;
start();
}
public void think(){
try {
System.out.println("哲学家"+Thread.currentThread().getId()+"思考问题");
sleep(13); //思考时间越长越不容易发生死锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void eat(){
synchronized(leftChopStick){
System.out.println(leftChopStick +"-- waiting for -- "+rightChopStick);
synchronized (rightChopStick){
System.out.println(this+"-----now eating");
}
}
}
@Override
public String toString() {
return "Philosopher"+number;
}
@Override
public void run() {
while (true){
think();
eat();
}
}
}
运行结果:
在上述程序中,防止死锁最容易的方式就是破坏第四个条件,原因:哲学家都在试图按照特定的顺序拿起筷子,先左后右,就可能会发生“每个人拿起右边的筷子,并等待左边的筷子”的情况,这就是循环等待条件。如果最后一个哲学家,先拿左边的筷子,再拿右边的筷子。那么这个哲学家就不会阻止右边的哲学家拿起左右两只筷子。
并发编程这些知识点唯有多练习,并观察其结果,才能去理解掌握。
参考:《java编程思想》
《java并发编程之美》
肖海鹏老师的教学视频