基础篇 --死锁之哲学家就餐问题实现

死锁:是指两个或者两个以上的线程在执行过程中,因为争夺资源而造成的互相等待的现象,在无外力的情况下,这些线程会一直相互等待而无法继续运行下去(摘自《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并发编程之美》

            肖海鹏老师的教学视频

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值