浅墨散人 浅墨散人
  • 基础
  • 设计模式
  • JVM
  • Maven
  • SpringBoot
  • 基础
  • Flask
  • Diango
  • Pandas
  • SqlAlchemy
  • Sqoop
  • Flume
  • Flink
  • Hadoop
  • Hbase
  • Hive
  • Kafka
  • Kylin
  • Zookeeper
  • Tez
  • MySQL
  • Doris
  • Chrome
  • Eclipse
  • IDEA
  • iTerm2
  • Markdown
  • SublimeText
  • VirtualBox
  • WebStrom
  • Linux
  • Mac
  • Hexo
  • Git
  • Vue
  • VuePress
  • 区块链
  • 金融
数据仓库
数据治理
读书笔记
关于我
GitHub (opens new window)
  • 基础
  • 设计模式
  • JVM
  • Maven
  • SpringBoot
  • 基础
  • Flask
  • Diango
  • Pandas
  • SqlAlchemy
  • Sqoop
  • Flume
  • Flink
  • Hadoop
  • Hbase
  • Hive
  • Kafka
  • Kylin
  • Zookeeper
  • Tez
  • MySQL
  • Doris
  • Chrome
  • Eclipse
  • IDEA
  • iTerm2
  • Markdown
  • SublimeText
  • VirtualBox
  • WebStrom
  • Linux
  • Mac
  • Hexo
  • Git
  • Vue
  • VuePress
  • 区块链
  • 金融
数据仓库
数据治理
读书笔记
关于我
GitHub (opens new window)
  • Java基础

    • Java基础
    • CSS自定义滚动条样式
    • Java发送短信
    • 线程的优先级
    • 线程的同步之Synchronized在单例模式中的应用
    • 线程的同步之Synchronized的使用
    • 线程的基本概念
    • 线程的死锁
      • 一、介绍
        • 1.1 什么情况会发生死锁?
      • 二、解决线程死锁问题:生产者消费者模式
        • 2.1 未使用生产者消费者模式情况
        • 2.2 加入生产者消费者模式:使用信号灯法
      • 三、总结
        • 3.1 什么情况下会出现死锁?
        • 3.2 死锁的解决办法?
        • 3.3 wait()、notify()、notifyAll()都是Object对象的方法
    • 线程的状态和常用操作
  • Java
  • Basic
2016-03-29
目录

线程的死锁

# 一、介绍

# 1.1 什么情况会发生死锁?

过多的同步方法会造成死锁 一旦有多个进程,且它们都要争用对多个锁的独占访问,那么就有可能发生死锁。如果有一组进程或线程,其中每个都在等待一个只有其它进程或线程才可以执行的操作,那么就称它们被死锁了。 最常见的死锁形式是当线程 1 持有对象 A 上的锁,而且正在等待对象 B 上的锁;而线程 2 持有对象 B 上的锁,却正在等待对象 A 上的锁。这两个线程永远都不会获得第二个锁,或是释放第一个锁,所以它们只会永远等待下去。 例如:黑帮交货 A:你先给钱,我才给货 B:你先给货,我再给钱 .....死锁了.... 实例代码:

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("先给货、、");
    }
}
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

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

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

生产者消费者模式(Producer-consumer problem)并不是设计模式:也称有限缓冲问题。 既然是生产者消费者模式,那必然有生产者类和消费者类,然后还有一个共享的资源(因为多个线程对同一份资源都需要锁释放所以才造成死锁) 所以,至少需要4个类:这里以在电影院看电影为例子说明生产者和消费者模式 共享资源(电影):Movie 生产者(电影院):Player 消费者(看电影的人):Watcher 测试类:App

# 2.1 未使用生产者消费者模式情况

共享资源类:Movie

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

生产者类:Player

//生产者:生产电影
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("右白虎");
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

消费者类:Watcher

//消费者:观看电影
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();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

测试代码:App

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

目前还没有加入生产者消费者模式,测试结果:打印的全是右白虎 右白虎 右白虎 ............. 加入现在加上锁,在共享资源上都加上锁再试试

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

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

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

对共享资源上加入flag标志来控制共享的资源flag=true:生产者生产,消费者等待【Object对象的wait()】。生产完成后,通知消费者消费【Object对象的notify()】flag=false:消费者消费,生产者等待【Object对象的wait()】。消费完成后,通知生产者生产【Object对象的notify()】这里用到了等待wait()和通知notify(),这俩都是Object对象的方法 。wait:会释放锁 Thread.sleep():不会释放锁【抱着锁睡觉】 接下来修改共享资源

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
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

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

# 三、总结

# 3.1 什么情况下会出现死锁?

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

# 3.2 死锁的解决办法?

使用生产者消费者模式【该模式不是一个设计模式,而是为了解决死锁而出现的一种策略】 生产者消费者模式使用之一:信号灯法 在共享资源上加入flag标志来控制当生产者生产的时候,消费者等待。 当消费者消费的时候,生产者等待。

# 3.3 wait()、notify()、notifyAll()都是Object对象的方法

所以所有对象都可以直接使用

#java#Thread
最后更新时间: 2022/7/23 10:17:11
线程的基本概念
线程的状态和常用操作

← 线程的基本概念 线程的状态和常用操作→

最近更新
01
分区分桶
08-21
02
数据模型(重要)
08-21
03
安装和编译
08-21
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式