• 线程的死锁


      一、介绍       

    什么情况会发生死锁?过多的同步方法会造成死锁

    一旦有多个进程,且它们都要争用对多个锁的独占访问,那么就有可能发生死锁。如果有一组进程或线程,其中每个都在等待一个只有其它进程或线程才可以执行的操作,那么就称它们被死锁了。

    最常见的死锁形式是当线程 1 持有对象 A 上的锁,而且正在等待对象 B 上的锁;而线程 2 持有对象 B 上的锁,却正在等待对象 A 上的锁。这两个线程永远都不会获得第二个锁,或是释放第一个锁,所以它们只会永远等待下去。

    例如:黑帮交货

    A:你先给钱,我才给货

    B:你先给货,我再给钱

    .....死锁了....

    实例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    public class LockDemo1 {
        public static void main(String[] args) {
            Object g = new Object();
            Object m = new Object();
            Test1 t1 = new Test1(g,m);
            Test1 t2 = new Test1(g,m);
            Thread thread1 = new Thread(t1);
            Thread thread2 = new Thread(t2);
            thread1.start();
            thread2.start();
        }
    }
    //线程1:先给钱
    class Test1 implements Runnable{
        Object goods;
        Object money;
        public Test1(Object goods, Object money) {
            this.goods = goods;
            this.money = money;
        }
        @Override
        public void run() {
            while (true) {
                test();
            }
        }
        public void test(){
            synchronized (goods) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (money) {
                }
            }
            System.out.println("先给钱、、");
        }
    }
    //线程2:先给货
    class Test2 implements Runnable{
        Object goods;
        Object money;
        public Test2(Object goods, Object money) {
            this.goods = goods;
            this.money = money;
        }
        @Override
        public void run() {
            while (true) {
                test();
            }
        }
        public void test(){
            synchronized (money) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (goods) {
                }
            }
            System.out.println("先给货、、");
        }
    }

    这样运行的话有可能被锁定,记住:并不是每次都是死锁的


     二、解决线程死锁问题:生产者消费者模式       

    生产者消费者模式(Producer-consumer problem)并不是设计模式:也称有限缓冲问题。

    既然是生产者消费者模式,那必然有生产者类和消费者类,然后还有一个共享的资源(因为多个线程对同一份资源都需要锁释放所以才造成死锁)

    所以,至少需要4个类:这里以在电影院看电影为例子说明生产者和消费者模式

    共享资源(电影):Movie

    生产者(电影院):Player

    消费者(看电影的人):Watcher

    测试类:App

    1、未使用生产者消费者模式情况

    共享资源类:Movie

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //共享资源
    public class Movie {
        private String pic ;
        //播放
        public void play(String pic){
            this.pic = pic;
        }
        //观看
        public void watch(){
            System.out.println(pic);
        }
    }

    生产者类:Player

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //生产者:生产电影
    public class Player implements Runnable {
        private Movie m;//共同使用的资源
        public Player(Movie m) {
            super();
            this.m = m;
        }
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                if (0==i%2) {
                    m.play("左青龙");
                }else{
                    m.play("右白虎");
                }
            }
        }
    }

    消费者类:Watcher

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //消费者:观看电影
    public class Watcher implements Runnable{
        private Movie m;//共同使用的资源
        public Watcher(Movie m) {
            super();
            this.m = m;
        }
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                m.watch();
            }
        }
    }

    测试代码:App

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class App {
        public static void main(String[] args) {
            Movie m= new Movie();
            Player p = new Player(m);
            Watcher w = new Watcher(m);
            new Thread(p).start();
            new Thread(w).start();
        }
    }

    目前还没有加入生产者消费者模式,测试结果:打印的全是右白虎

    右白虎

    右白虎

    .............

    加入现在加上锁,在共享资源上都加上锁再试试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Movie {
        private String pic ;
        //播放
        public synchronized void play(String pic){//加锁
            this.pic = pic;
        }
        //观看
        public synchronized void watch(){//加锁
            System.out.println(pic);
        }
    }

    再次测试,结果还是只有右白虎。


    2、加入生产者消费者模式:使用信号灯法

    对共享资源上加入flag标志来控制共享的资源

    flag=true:生产者生产,消费者等待【Object对象的wait()】。生产完成后,通知消费者消费Object对象的notify()】

    flag=false:消费者消费,生产者等待Object对象的wait()】。消费完成后,通知生产者生产Object对象的notify()】

    这里用到了等待wait()和通知notify(),这俩都是Object对象的方法 。wait:会释放锁        Thread.sleep():不会释放锁【抱着锁睡觉】

    接下来修改共享资源

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    public class Movie {
        private String pic ;
        /**
         * flag=true:生产者生产,消费者等待【Object对象的wait()】。生产完成后,通知消费者消费【Object对象的notify()】
         * flag=false:消费者消费,生产者等待【Object对象的wait()】。消费完成后,通知生产者生产【Object对象的notify()】
         */
        private boolean flag=true;//信号灯
        //播放:生产者
        public synchronized void play(String pic){
            if (!flag) {//生产者等待
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //开始生产
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //生产完毕
            this.pic = pic;
            System.out.println("生产了:"+pic);
            //通知消费者消费
            this.notify();
            //生产者停止生产(让其等待):因为刚生产完了就不必生产了
            this.flag = false;
        }
        //观看:消费者
        public synchronized void watch(){
            if (flag) {//消费者等待
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //开始消费
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("消费了:"+pic);//消费完毕
            //通知生产者可以生产了
            this.notifyAll();
            //消费停止(让其等待):因为已经消费完毕了
            this.flag = true;
        }
    }

    再次测试:

    生产了:左青龙

    消费了:左青龙

    生产了:右白虎

    消费了:右白虎

    测试结果很规律:生产了什么就消费什么,等消费完成了就继续再生产...............没有出现死锁问题


     三、总结       

    1、什么情况下会出现死锁?

    当线程 1 持有对象 A 上的锁,而且正在等待对象 B 上的锁;而线程 2 持有对象 B 上的锁,却正在等待对象 A 上的锁。这两个线程永远都不会获得第二个锁,或是释放第一个锁,所以它们只会永远等待下去。

    过多的使用同步会出现死锁

    2、死锁的解决办法?

    使用生产者消费者模式【该模式不是一个设计模式,而是为了解决死锁而出现的一种策略】

    生产者消费者模式使用之一:信号灯法

    在共享资源上加入flag标志来控制当生产者生产的时候,消费者等待。

    当消费者消费的时候,生产者等待。

    3、wait()、notify()、notifyAll()都是Object对象的方法,所以可以直接使用







  • 相关阅读:
    day 50 jquery之看我操作
    day 49 JavaScript中BOM和DOM
    day 43 CSS前端
    day 42 前端HTML
    day 41 mysql索引以及前端的简介
    day 40 mysql 之视图,触发器,事务,存储过程及函数
    day 39数据库mysql之多表查询
    day 38 数据库MySQL之单表查询
    day 37 数据库MySQL基本操作
    39套漂亮的后台模板
  • 原文地址:https://www.cnblogs.com/meet/p/5290953.html
Copyright © 2020-2023  润新知