死锁


什么情况下导致线程死锁,遇到线程死锁该怎么解决?

死锁的定义

所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进

死锁产生的必要条件

  • 互斥条件:线程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某 资源仅为一个线程所占有。此时若有其他线程请求该资源,则请求线程只能等待。
  • 不剥夺条件:线程所获得的资源在未使用完毕之前,不能被其他线程强行夺走,即只能由获得该资源的线程自己来释放(只能是主动释放)。
  • 请求和保持条件:线程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他线程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
  • 循环等待条件:存在一种线程资源的循环等待链,链中每一个线程已获得的资源同时被链中下一个线程所请求。即存在一个处于等待状态的线程集合{Pl, P2, …, pn},其中 Pi 等待的资源被 P(i+1)占有(i=0, 1, …, n-1),Pn 等待的资源被 P0 占有,如图 2-15 所示。
  • 在这里插入图片描述

产生死锁的一个例子

package com.wjl.test.死锁;

public class DeadLock implements  Runnable{

    public  int flag=1;
    //静态对象是类的所有对象共享的
    private static Object o1=new Object(),o2=new Object();

    @Override
    public void run() {
        System.out.println("flag = "+flag);
        if(flag==1){
            synchronized (o1){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o2){
                    System.out.println("1");
                }
            }
        }
        if(flag==0){
            synchronized (o2){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o1){
                    System.out.println("0");
                }
            }
        }
    }

    public static void main(String[] args) {
        DeadLock td1=new DeadLock();
        DeadLock td2=new DeadLock();
        td1.flag=1;
        td2.flag=0;
       //td1,td2 都处于可执行状态,但 JVM 线程调度先执行哪个线程是不确定的。
        //td2 的 run()可能在 td1 的 run()之前运行
        new Thread(td1).start();
        new Thread(td2).start();

    }
}

当 DeadLock 类的对象 flag1 时(td1),先锁定 o1,睡眠 500 毫秒
而 td1 在睡眠的时候另一个 flag
0 的对象(td2)线程启动,先锁定 o2,睡眠 500 毫秒
td1 睡眠结束后需要锁定 o2 才能继续执行,而此时 o2 已被 td2 锁定;
td2 睡眠结束后需要锁定 o1 才能继续执行,而此时 o1 已被 td1 锁定;
td1、td2 相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁。

如何避免死锁

在有些情况下死锁是可以避免的。两种用于避免死锁的技术

1)加锁顺序(线程按照一定的顺序加锁)

package com.wjl.test.死锁;

/**
 * 如何避免死锁
 * 加锁顺序(线程按照一定的顺序加锁)
 */
public class DeadLock1 {
    public int flag = 1;
    //静态对象是类的所有对象共享的
    private static Object o1 = new Object(), o2 = new Object();

    public void money(int flag) {
        this.flag = flag;
        if (flag == 1) {
            synchronized (o1) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o2) {
                    System.out.println("当前的线程是" + Thread.currentThread().getName() + " " + "flag 的值" + "1");
                }
            }
        }
        if (flag == 0) {
            synchronized (o2) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o1) {
                    System.out.println("当前的线程是" + Thread.currentThread().getName() + " " + "flag 的值" + "0");
                }
            }
        }


    }

    public static void main(String[] args) {
        final DeadLock1 td1=new DeadLock1();
        final DeadLock1 td2=new DeadLock1();
        td1.flag=1;
        td2.flag=2;
        //td1,td2 都处于可执行状态,但 JVM 线程调度先执行哪个线程是不确定的。
        //td2 的 run()可能在 td1 的 run()之前运行
        final Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                td1.flag=1;
                td1.money(1);
            }
        });
        t1.start();
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    //让 t2 等待 t1 执行完
                    t1.join();//核心代码,让 t1 执行完后 t2 才会执行
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                td2.flag = 0;
                td1.money(0);
            }
        });
        t2.start();
    }
}

结果:
当前的线程是Thread-0 flag 的值1
当前的线程是Thread-1 flag 的值0

2)加锁时限

线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁

package com.wjl.test.死锁;

import java.sql.Time;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 如何避免死锁
 * 加锁顺序(线程按照一定的顺序加锁)
 */
public class DeadLock2 {
    public int flag = 1;
    //静态对象是类的所有对象共享的
    private static Object o1 = new Object(), o2 = new Object();

    public void money(int flag) throws InterruptedException {
        this.flag = flag;
        if (flag == 1) {
            synchronized (o1) {

                    Thread.sleep(500);

                synchronized (o2) {
                    System.out.println("当前的线程是" + Thread.currentThread().getName() + " " + "flag 的值" + "1");
                }
            }
        }
        if (flag == 0) {
            synchronized (o2) {

                    Thread.sleep(500);

                synchronized (o1) {
                    System.out.println("当前的线程是" + Thread.currentThread().getName() + " " + "flag 的值" + "0");
                }
            }
        }


    }

    public static void main(String[] args) {
        final Lock lock = new ReentrantLock();
        final DeadLock2 td1=new DeadLock2();
        final DeadLock2 td2=new DeadLock2();
        td1.flag=1;
        td2.flag=2;
        //td1,td2 都处于可执行状态,但 JVM 线程调度先执行哪个线程是不确定的。
        //td2 的 run()可能在 td1 的 run()之前运行
        final  Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                String tName=Thread.currentThread().getName();
                td1.flag=1;

                try {
                    //获取不到锁,就等 5 秒,如果 5 秒后还是获取不到就返回 false
                    if(lock.tryLock(5000, TimeUnit.MILLISECONDS)){
                        System.out.println(tName + "获取到锁!");
                    }else{
                        System.out.println(tName + "获取不到锁!");
                        return;
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }

                try {
                    td1.money(1);
                } catch (InterruptedException e) {
                    System.out.println(tName +"出错了!!!");
                }finally {
                    System.out.println("当前线程是:"+Thread.currentThread().getName()+"释放锁!!");
                    lock.unlock();
                }
            }
        });

        t1.start();

        Thread t2= new Thread(new Runnable(){

            @Override
            public void run() {
                String tName = Thread.currentThread().getName();
                td1.flag = 1;

                try {
                    //获取不到锁,就等 5 秒,如果 5 秒后还是获取不到就返回 false
                    if(lock.tryLock(5000,TimeUnit.MILLISECONDS)){
                        System.out.println(tName+"获取到锁!");
                    }else{
                        System.out.println(tName+"获取不到锁");
                        return;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    td2.money(0);
                } catch (InterruptedException e) {
                    System.out.println(tName + "出错了!!!");
                }finally {
                    System.out.println("当前的线程是"+Thread.currentThread().getName()+"释放锁!!");
                    lock.unlock();
                }
            }
        });
        t2.start();
    }
}

运行结果:
Thread-0获取到锁!
当前的线程是Thread-0 flag 的值1
当前线程是:Thread-0释放锁!!
Thread-1获取到锁!
当前的线程是Thread-1 flag 的值0
当前的线程是Thread-1释放锁!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值