11.线程死锁问题
线程死锁是一定要规避的问题!
-
问题:
- 两个小孩都喜欢对方的玩具
- 唐唐:你把你的玩具鸭借我玩,我就把芭比娃娃给你玩
- 豆豆:你把你的芭比娃娃借给我,我就把玩具鸭借给你
- 造成了僵局的状态,谁都不把自己的玩具给对方,这就是死锁
-
案例1
-
唐唐类
public class TangTang implements Runnable { Object babi = new Object();//芭比娃娃 Object duck = new Object();//玩具鸭 @Override public void run() { synchronized (babi) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (duck) { } } System.out.println("唐唐愿意把芭比娃娃给豆豆玩"); } }
-
豆豆类
public class DouDou implements Runnable { Object babi = new Object();//芭比娃娃 Object duck = new Object();//玩具鸭 @Override public void run() { synchronized (duck) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (babi) { } } System.out.println("豆豆愿意把玩具给唐唐玩"); } }
-
测试
public class Test { public static void main(String[] args) { Thread tangtang = new Thread(new TangTang()); Thread doudou = new Thread(new DouDou()); tangtang.start(); doudou.start(); } }
-
结果
豆豆愿意把玩具给唐唐玩 唐唐愿意把芭比娃娃给豆豆玩
-
因为这里tangtang和doudou类拿到的babi和duck分别是不同的对象,没有实现线程同步,因此能够运行出来,而我们的业务需求是希望能够得到同一个babi和同一个duck对象,所以看下面案例:
-
唐唐类
public class TangTang implements Runnable { Object babi ;//芭比娃娃 Object duck;//玩具鸭 public TangTang(Object duck, Object babi) { this.duck = duck; this.babi = babi; } @Override public void run() { synchronized (babi) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (duck) { } } System.out.println("唐唐愿意把芭比娃娃给豆豆玩"); } }
-
豆豆类
public class DouDou implements Runnable { Object babi ;//芭比娃娃 Object duck ;//玩具鸭 public DouDou(Object duck, Object babi) { this.duck = duck; this.babi = babi; } @Override public void run() { synchronized (duck) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (babi) { } } System.out.println("豆豆愿意把玩具给唐唐玩"); } }
-
测试
public class Test { public static void main(String[] args) { Object babi = new Object();//芭比娃娃 Object duck = new Object();//玩具鸭 Thread tangtang = new Thread(new TangTang(duck,babi)); Thread doudou = new Thread(new DouDou(duck,babi)); tangtang.start(); doudou.start(); //结果:什么也没出来 /* * 产生了死锁 * 原因:当锁嵌套的时候,两个线程共同操作同一份资源的时候他们各自都有一把锁 * 各自都不释放自己的锁但是同时又想得到对方的锁,因此就陷入了僵持状态 * 死锁是我们必须要规避的问题,尽可能不要在同步代码块中在去嵌套同步代码块(尽可能规避锁再去嵌套) */ } }
-
产生死锁的原因:
- 当两个线程操作同一份资源时,他们各自都有一把锁,但是在没有执行完代码(为释放自己的锁)的时候就想要得到对方的锁,因此陷入僵持状态
-
产生死锁的条件
- 两个或两个以上的线程在活动
- 某个线程在执行自己任务没有执行完的情况下就想获取第二个锁,造成了锁的嵌套
-
如何避免死锁
- 当前线程一定要先释放自己的锁(执行完线程同步的方法的代码)后再去执行其他的锁(其他的线程同步的方法)
- 尽量不要锁嵌套,不要在同步代码块里面再去嵌套同步代码块
-
死锁主要怕面试问到,应该尽量规避不要造成死锁!
-
12.生产者和消费者模型以及解决方案
这是一道面试题
-
生产者和消费者问题,生产者不断生产产品,消费者不断取走生产者生产的产品
-
生产者每生产出一个产品后将其放到一个区域之中,之后消费者紧接着就从该区域取走产品
-
案例需求
- 一个轮流录入和读取电影信息的程序
- 变形金刚----一部科幻电影
- 神偷奶爸-----一部3D动画片
- 生产者:录入信息
- 消费者:读取信息
- 一个轮流录入和读取电影信息的程序
-
案例1:
-
电影
public class Movie { private String name; private String info; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getInfo() { return info; } public void setInfo(String info) { this.info = info; } }
-
生产者
/** * 生产者线程,负责生产产品 * @author Administrator * */ public class Producer implements Runnable{ private Movie movie =null; boolean flag = true; public Producer(Movie movie) { this.movie=movie; } @Override public void run() { for (int i = 1; i <= 50; i++) { if(flag) { this.movie.setName("变形金刚"); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.movie.setInfo("一部科幻电影"); flag=false; }else { this.movie.setName("神偷奶爸"); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.movie.setInfo("一部3D动画片"); flag = true; } } } }
-
消费者
/** * 消费者线程:负责取走产品 * @author Administrator * */ public class Consumer implements Runnable{ private Movie movie; public Consumer(Movie movie) { this.movie = movie; } @Override public void run() { for (int i = 1; i <= 50; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(this.movie.getName()+"---"+this.movie.getInfo()); } } }
-
测试
public class Test { public static void main(String[] args) { Movie movie = new Movie(); Thread producer = new Thread(new Producer(movie)); Thread comsumer = new Thread(new Consumer(movie)); producer.start(); comsumer.start(); } }
-
结果
神偷奶爸---一部科幻电影 变形金刚---一部3D动画片 神偷奶爸---一部科幻电影 神偷奶爸---一部3D动画片 神偷奶爸---一部科幻电影 变形金刚---一部3D动画片 变形金刚---一部3D动画片 变形金刚---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 神偷奶爸---一部科幻电影 变形金刚---一部3D动画片 变形金刚---一部3D动画片 神偷奶爸---一部3D动画片 变形金刚---一部3D动画片 神偷奶爸---一部科幻电影 变形金刚---一部科幻电影 变形金刚---一部3D动画片 变形金刚---一部3D动画片 变形金刚---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 变形金刚---一部3D动画片 变形金刚---一部科幻电影 变形金刚---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部科幻电影 神偷奶爸---一部3D动画片 神偷奶爸---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 神偷奶爸---一部科幻电影 神偷奶爸---一部科幻电影 变形金刚---一部科幻电影 神偷奶爸---一部科幻电影 神偷奶爸---一部科幻电影 神偷奶爸---一部科幻电影 神偷奶爸---一部科幻电影 神偷奶爸---一部3D动画片
-
原因
- 当生产者在生产数据的时候,因为赋值有两步操作,很有可能是在赋值name的时候就被其他线程抢占还没来得及更新info里面的数据.所以造成了数据不安全
- 建议:最好是同步操作
-
-
案例2:增加线程同步
-
电影
public class Movie { private String name; private String info; public String getName() { return name; } public String getInfo() { return info; } public synchronized void set(String name,String info) { this.name = name; try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.info = info; } public synchronized void get() { System.out.println(this.getName()+"---"+this.getInfo()); } }
-
生产者
/** * 消费者线程:负责取走产品 * @author Administrator * */ public class Consumer implements Runnable{ private Movie movie; public Consumer(Movie movie) { this.movie = movie; } @Override public void run() { for (int i = 1; i <= 50; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // System.out.println(this.movie.getName()+"---"+this.movie.getInfo()); this.movie.get(); } } }
-
消费者
/** * 生产者线程,负责生产产品 * @author Administrator * */ public class Producer implements Runnable{ private Movie movie =null; boolean flag = true; public Producer(Movie movie) { this.movie=movie; } @Override public void run() { for (int i = 1; i <= 50; i++) { if(flag) { this.movie.set("变形金刚","一部科幻电影"); flag=false; }else { this.movie.set("神偷奶爸","一部3D动画片"); flag = true; } } } }
-
测试
/** * 通过线程同步,使数据更安全 * 问题:得到的结果没有交替执行,在插入的时候两个电影并没有交替去插入,所以取到的也不是交替的 * 原因:当生产者生产了一个电影的时候,由于消费者的线程执行的比较快,所以消费者线程执行了多次 * 所以造成了生产者和消费者没有交替执行 * @author Administrator * */ public class Test { public static void main(String[] args) { Movie movie = new Movie(); Thread producer = new Thread(new Producer(movie)); Thread comsumer = new Thread(new Consumer(movie)); producer.start(); comsumer.start(); } }
-
结果
神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 变形金刚---一部科幻电影 变形金刚---一部科幻电影 变形金刚---一部科幻电影 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 变形金刚---一部科幻电影 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片 神偷奶爸---一部3D动画片
-
原因
- 当生产者生产了一个电影的时候,由于消费者的线程执行的比较快,所以消费者线程执行了多次
- 所以造成了生产者和消费者没有交替执行
-
-
案例3:通过object类的notify()和wait()方法,唤醒和等待线程。让生产者每生产好一件产品后等待,并同时唤醒消费者线程获取商品
-
电影
public class Movie { private String name; private String info; private boolean flag = true;//设置标识位 public String getName() { return name; } public String getInfo() { return info; } /** * 每方生产者线程生产了产品后,就唤醒消费者线程获取产品,生产者线程需要等待消费者线程获取产品后再去执行生产者线程生产产品 * @param name * @param info */ public synchronized void set(String name,String info) { if(!flag) { try { super.wait();//让当前线程等待(调用object类的wait方法) } catch (InterruptedException e) { e.printStackTrace(); } } //生产产品 this.name = name; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } this.info = info; flag=false;//重置标识位,让消费者消费 super.notify();//唤醒在等待在执行的线程(消费者线程) } public synchronized void get() { if(flag) { try { //当生产者在生产的时候.让消费者线程等待 super.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }else { System.out.println(this.getName()+"---"+this.getInfo()); flag = true;//标识为true,唤醒生产者线程 super.notify(); } } }
-
生产者
/** * 生产者线程,负责生产产品 * @author Administrator * */ public class Producer implements Runnable{ private Movie movie =null; boolean flag = true; public Producer(Movie movie) { this.movie=movie; } @Override public void run() { for (int i = 1; i <= 50; i++) { if(flag) { this.movie.set("变形金刚","一部科幻电影"); flag=false; }else { this.movie.set("神偷奶爸","一部3D动画片"); flag = true; } } } }
-
消费者
/** * 消费者线程:负责取走产品 * @author Administrator * */ public class Consumer implements Runnable{ private Movie movie; public Consumer(Movie movie) { this.movie = movie; } @Override public void run() { for (int i = 1; i <= 50; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // System.out.println(this.movie.getName()+"---"+this.movie.getInfo()); this.movie.get(); } } }
-
测试
public class Test { public static void main(String[] args) { Movie movie = new Movie(); Thread producer = new Thread(new Producer(movie)); Thread comsumer = new Thread(new Consumer(movie)); producer.start(); comsumer.start(); } }
-
结果
变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片 变形金刚---一部科幻电影 神偷奶爸---一部3D动画片
-