线程的死锁

   一、介绍       

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

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

最常见的死锁形式是当线程 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对象的方法,所以可以直接使用







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值