死锁详解

什么是死锁

死锁是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

死锁的必要条件

  • 1)互斥条件:某个资源某一时刻只能由一个线程占用
  • 2)不可抢占条件:资源申请者不能从资源占有者手中抢夺资源
  • 3)占有且申请:线程至少已经占有一个资源,但又去申请新的资源,新资源被占用,该线程会阻塞
  • 4)循环等待:一个线程等待另外一个线程释放资源,另外一个线程又去等待,直到最后一个线程等待第一个线程释放资源,使得大家都被锁住

常见死锁

1)交叉锁

一个线程A 访问R1(获得锁R1),然后又访问R2;另一个先线程B 访问R2(锁住了R2),然后企图访问R1;这时线程A由于线程B已经锁住R2,它必须等待线程B释放R2才能继续,同样线程B要等线程A释放R1才能继续,这就死锁就产生了。


```java
    private final Object R1 = new Object();
    private final Object R2 = new Object();

    //线程A
    public void func1(){
        synchronized (R1){
            synchronized (R2){
                //do something
            }
        }
    }

    //线程B
    public void func2(){
        synchronized (R2){
            synchronized (R1){
                //do something
            }
        }
    }

2)内存不足

线程A和线程B都需要内存40MB,现在线程A已有10MB,还需要300MB;线程B已有10MB,还需要30MB,但是系统内存只剩余20MB,因此两个线程都在等对方释放内存来满足自己,死锁就产生了

3)一问一答的数据交换

服务器端(server)和客户端(client),server等待client的访问;client发送请求到server,等待接收数据;由于网络的原因,server错过了client的请求,因此server等待client的访问,client等待接收数据,产生死锁

4)死循环引起的死锁

有关开源,比如使用HashMap时,就会引起死锁

    private static HashMap<String, String> map = new HashMap<>();
        for(int i=0; i<2; i++){
            new Thread(){
                @Override
                public void run() {
                    for(int i=0; i<Integer.MAX_VALUE; i++ ){
                        map.put(String.valueOf(i), String.valueOf(i));
                    }
                }
            }.start();
        }

如何避免死锁

死锁的预防

  • 1)允许某些线程同时访问某些资源
  • 2)允许线程强行去夺取某些资源
  • 3)允许实现资源预分配策略
  • 4)线程按照编号申请资源

死锁的避免

安全序列: 如果系统按照这种序列去分配资源,每个线程都能够顺序完成自己的需求,这样序列就是安全序列,系统称之为安全状态。安全序列可能会有多个。

  • 如果系统处于安全状态,就一定不会发生死锁;如果系统处于不安全状态,就"可能"会发生死锁。

解决哲学家就餐的死锁问题

有五位哲学家,身边各有一根筷子,如何才能让五个哲学家都就上餐,哲学家在就餐的时候必须保证左右两边筷子都可以使用

class Chopstick{
    //key为筷子的编号,value是筷子的标识别
    private 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(){
        String curName = Thread.currentThread().getName();

        int leftChop = Integer.parseInt(curName); //左边筷子
        int rightChop = (leftChop+1) % 5; //右边筷子

        //判断左右两边的筷子是否都可用
        while(map.get(leftChop) || map.get(rightChop)){
            try {
                this.wait();//表示两双筷子未能同时可用,哲学家陷入思考,线程阻塞
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        map.put(leftChop, true);
        map.put(rightChop, true);

        System.out.println("Thread "+curName+" got the chopsticks "+leftChop+" and "+rightChop);

    }

    public synchronized void freeChopstick(){
        String curName = Thread.currentThread().getName();
        int leftChop = Integer.parseInt(curName); //左边筷子
        int rightChop = (leftChop+1) % 5; //右边筷子
        map.put(leftChop, false);
        map.put(rightChop, false);
        this.notifyAll();
    }
}

public class TestDemo8 {
    public static void main(String[] args) {
        Chopstick chopstick = new Chopstick();
        for(int i=0; i<5; i++){
            new Thread(String.valueOf(i)){
                @Override
                public void run() {
                    //当前线程获得两边筷子,准备就餐
                  //  while(true){
                        chopstick.getChopstick();
                        System.out.println("Thread " +Thread.currentThread().getName()+" is eating");
                        try {

                            TimeUnit.MILLISECONDS.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        chopstick.freeChopstick();
               //     }
                }
            }.start();
        }
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值