JAVA多线程(八)--死锁-哲学家吃面问题

1.哲学家吃面问题:也就是说,有五个哲学家围成一圈,然后桌子上有五根筷子,每个人旁边都有一根筷子。这里就引出来了一个死锁问题,每个人都拥有其他人所需要的资源,但同时又等待其他人所拥有的资源,并且每个人在获得所有资源之前都不会放弃自己所拥有的资源。
2.常见的死锁问题:
1) 交叉锁导致的程序出现死锁,看一个例子

public class TestDemo2 {
    //交叉锁导致的程序出现死锁
    private final Object R1 = new Object();
    private final Object R2 = new Object();

    public void func1(){
        synchronized (R1){
            synchronized (R2){
                //dosomething
            }
        }
    }

    public void func2(){
        synchronized (R2){
            synchronized (R1){
                //dosomething
            }
        }
    }
    public static void main(String[] args) {

    }
}

在这个例子中,我们可以看到A此时拥有了R1的锁,然后它在等待R2的锁,但是这时线程B拥有R2的锁,它在等待R1的锁,这时线程A和线程B都持有对方所需要的锁,但是谁都不会放开自己现在所持有的的锁,进入了无限的等待,这就形成了死锁问题。
2) 内存不足
假如有两个线程,threadA 获得了10MB memory , threadB 获得了20MB memory ,两个线程要执行的话都需要30MB ,但是此时系统可用内存只剩余20MB,这就会造成内存不足的状况。
3) 一问一答式的数据交换
比如server(服务器端)给client(客户端)开启了端口,等待client访问 ;client发送访问请求,等待接收;但是这时候server错过了client的请求,那么也就可能会造成死锁的问题。
4)死循环引起的死锁
(注意这个代码不要运行,否则可能会使得运行出现问题)
HashMap<String, String> map = new HashMap<>();
for(int i=0; i<2; i++){
new Thread(){
public void run(){
for(i = 1; i<Integer.MAX_VALUE; i++){
map.put(String.valueOf(i), String.valueOf(i));
}
}
}.start();
}
3.死锁问题发生的必要条件
1)互斥条件:每个资源在某一段时间只能由一个线程占用
2)不可抢占条件:线程所获得的资源在未使用完毕之前,其他线程不能强行去夺取
3)占有且申请条件:线程至少已经占有一个资源,又申请新的资源,该资源被另外的线程占有,该线程阻塞
4)循环等待条件:一个线程等待其他线程释放资源,其他线程又在等待另外一个线程释放资源,且最后有一个线程在等待第一个线程释放资源,这就使得大家都被锁住
那么我们如何避免死锁:就只能说让代码逻辑不同时满足以上4条。
4.哲学家吃面的代码:

class ChopSticks{
    protected String name;

    public ChopSticks(String name){
        this.name = name;
    }
}

class PhilosopherThread extends Thread{
    private ChopSticks lefeChop;
    private ChopSticks rightChop;
    private String name;

    public PhilosopherThread(String name, ChopSticks lefeChop, ChopSticks rightChop){
        this.name = name;
        this.lefeChop = lefeChop;
        this.rightChop = rightChop;
    }

    @Override
    public void run() {
        //模拟吃面
        synchronized (lefeChop){
            System.out.println(name + " got the chopstick "+lefeChop.name);
            synchronized (rightChop){
                System.out.println(name + " got the chopstick "+rightChop.name);
                System.out.println(name + "is eating noodles");

                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(name + " release the chopstick "+lefeChop.name+" and "+rightChop.name);
        }
    }
}

public class TestDemo{
    public static void main(String[] args) {
        ChopSticks chopStick0 = new ChopSticks("0");
        ChopSticks chopStick1 = new ChopSticks("1");
        ChopSticks chopStick2 = new ChopSticks("2");
        ChopSticks chopStick3 = new ChopSticks("3");
        ChopSticks chopStick4 = new ChopSticks("4");

        new PhilosopherThread("thread0", chopStick0, chopStick1).start();
        new PhilosopherThread("thread1", chopStick1, chopStick2).start();
        new PhilosopherThread("thread2", chopStick2, chopStick3).start();
        new PhilosopherThread("thread3", chopStick3, chopStick4).start();
        new PhilosopherThread("thread4", chopStick4, chopStick0).start();
    }
}

这时会造成死锁问题,那么我们就需要用wait(),notify(),notifyAll()机制来解决,使得哲学家吃之前必须保证左右两边筷子都可使用然后再去吃,代码如下:

class Chopsticks{
    protected static HashMap<Integer, Boolean> map = new HashMap<>();

    static{
        map.put(0, false);
        map.put(1, false);
        map.put(2, false);
        map.put(3, false);
        map.put(4, false);
    }

    public synchronized void getChopStick(){
        //首先获取跟当前线程名相同的chopstick -》curNo
        String curName = Thread.currentThread().getName();

        int curNo = Integer.parseInt(curName);
        //另外一边即是nextNo = (curNo+1)%5
        int nextNo = (curNo+1)%5;
        //只要map.get(curNo)==true或者map.get(nextNo)==true
        while(map.get(curNo) || map.get(nextNo)){
            //当前线程阻塞  同时释放你所获得某一只筷子的使用权
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //被唤醒后使用筷子进行吃面
        map.put(curNo, true);
        map.put(nextNo, true);
        System.out.println("Thread "+curName+" got the chopstick "+curNo+" and "+nextNo  );
    }

    public synchronized void freeChopStick(){
        String curName = Thread.currentThread().getName();

        int curNo = Integer.parseInt(curName);
        int nextNo = (curNo+1)%5;
        map.put(curNo, false);
        map.put(nextNo, false);
        this.notifyAll();
    }
}

public class TestDemo{
    public static void main(String[] args) {
        Chopsticks chopSticks = new Chopsticks();

        for(int i=0; i<5; i++){
            new Thread(String.valueOf(i)){
                @Override
                public void run() {
                    while(true){
                        chopSticks.getChopStick();
                        try {
                            System.out.println("Thread "+Thread.currentThread().getName()+" is eating noodles");
                            TimeUnit.MILLISECONDS.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        chopSticks.freeChopStick();
                    }
                }
            }.start();
        }
    }
}

在这里插入图片描述
这时,发现哲学家吃面的死锁问题被解决了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值