生产者与消费者
代码要求知道做什么用即可
线程间的通讯问题以及 Object 类的支持
基础模型
现在希望实现一种数据的生产和取出的操作形式,即:有两个甚至更多的线程对象,这样的线程分为生产者线程和消费者线程
那么最理想的状态是生产者每生产完一条完整的数据之后,消费者就要取走这个数据,并且进行输出的打印
现在假设要输出的信息有这样两个:
title = 帅帅, content = 一个学生;
title = 可爱的小动物, content = 小猫咪;
范例:程序的初期实现
package cn.mysterious.actualcombat; class Info{ private String title; private String content; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } } class Producptor implements Runnable{ private Info info = null; public Producptor(Info info){ this.info = info; } @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 50; i++) { if (i % 2 == 0) { // 偶数 this.info.setTitle("帅帅"); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.info.setContent("一个学生"); }else { this.info.setTitle("可爱的动物"); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.info.setContent("小猫咪"); } } } } class Consumer implements Runnable{ private Info info = null; public Consumer(Info info){ this.info = info; } @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 50; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(this.info.getTitle() + "-->" + this.info.getContent()); } } } public class ActualCombat{ public static void main(String[] args){ Info info = new Info(); Producptor p = new Producptor(info); Consumer c = new Consumer(info); new Thread(p).start(); new Thread(c).start(); } }
通过以上的执行可以发现有两个问题:
第一:数据错位了
第二:重复生产,重复取出
解决数据不同步问题
要想解决同步问题一定使用同步代码块或者是同步方法,既然要同步,那么肯定要将设置属性和取得的属性的内容都统一交给 Info 完成
范例:
package cn.mysterious.actualcombat; class Info{ private String title; private String content; public synchronized void set(String content, String title){ this.title = title; try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.content = content; } public synchronized void get(){ try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(this.title + "-->" +this.content ); } } class Producptor implements Runnable{ private Info info = null; public Producptor(Info info){ this.info = info; } @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 50; i++) { if (i % 2 == 0) { // 偶数 this.info.set("帅帅","一个学生"); }else { this.info.set("可爱的动物","小猫咪"); } } } } class Consumer implements Runnable{ private Info info = null; public Consumer(Info info){ this.info = info; } @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 50; i++) { this.info.get(); } } } public class ActualCombat{ public static void main(String[] args){ Info info = new Info(); Producptor p = new Producptor(info); Consumer c = new Consumer(info); new Thread(p).start(); new Thread(c).start(); } }
所有的设置和取得数据的操作都交给了同步方法完成
现在同步问题解决了,但是重复问题更严重了
解决重复操作
如果要想解决重复问题,那么必须加入等待与唤醒的处理机制,而这样的操作方法是有 Object 类所提供的
在 Object 类中提供有如下几种方法:
等待: public final void wait() throws InterruptedException{}
唤醒第一个等待线程: public final void notify();
唤醒全部等待线程: public final void notifyAll();
范例:修改Info类
package cn.mysterious.actualcombat; class Info{ private String title; private String content; private boolean flag = true; // flag = true 表示可以生产,但是不允许取走数据 // flag = false 表示可以取走数据,但是不允许生产数据 public synchronized void set(String title, String content){ if (this.flag = false) { // 表示已经生产过了,还未取走 try { super.wait(); // 等待 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // 没有生产,可以生产 this.title = title; try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.content = content; this.flag = false;// 表示生产过了 super.notify(); } public synchronized void get(){ if (this.flag = true) { // 此时应该生产,不应该取走数据 try { super.wait(); // 等待 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(this.title + "-->" +this.content ); this.flag = true; // 表示取走了 super.notify(); } } class Producptor implements Runnable{ private Info info = null; public Producptor(Info info){ this.info = info; } @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 50; i++) { if (i % 2 == 0) { // 偶数 this.info.set("帅帅","一个学生"); }else { this.info.set("可爱的动物","小猫咪"); } } } } class Consumer implements Runnable{ private Info info = null; public Consumer(Info info){ this.info = info; } @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 50; i++) { this.info.get(); } } } public class ActualCombat{ public static void main(String[] args){ Info info = new Info(); Producptor p = new Producptor(info); Consumer c = new Consumer(info); new Thread(p).start(); new Thread(c).start(); } }
面试题:请解释 sleep() 与 wait() 的区别?
sleep() 是 Thread 类定义的方法,在休眠一定时间之后自己唤醒
wait() 是 Object 类定义的方法,表示线程要等待执行,必须通过 notify(),notifyAll() 方法来进行唤醒
总结
生产者和消费者这是一个模型,完整的体现了线程的同步, Object 类的支持