什么情况会发生死锁?过多的同步方法会造成死锁
一旦有多个进程,且它们都要争用对多个锁的独占访问,那么就有可能发生死锁。如果有一组进程或线程,其中每个都在等待一个只有其它进程或线程才可以执行的操作,那么就称它们被死锁了。
最常见的死锁形式是当线程 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对象的方法,所以可以直接使用