综合案例:生产者与消费者
- 综合案例:生产者与消费者
- 生产者与消费者基本程序模型
- 解决生产者与消费者同步问题
- 利用object类解决重复问题
综合案例:生产者与消费者
生产者与消费者基本程序模型
在多线程开发过程中最为著名的案例就是生产者与消费者操作
- 生产者负责信息内容的生产;
- 每当生产者生产完成一项完整的信息之后消费者就要从这里面取走信息;
- 如果生产者没有生产则消费者就要等待它完成生产,如果消费者还没有对信息进行消费,则生产者应对消费者信息处理完成后再进行生产;
程序的基本实现
可将生产者定义为两个独立的线程类对象
- 数据1:title=小杨、content=宇宙大帅哥;
- 数据2:title=小方、content=社会小青年;
生产者与消费者是两个独立的线程,那么这两个独立的线程之间就需要有一个数据保存的集中点,可以单独定义一个Message类实现数据的保存
范例:实现程序基本结构
public class ThreadDemo{
public static void main(String[] args) throws Exception {
Message msg=new Message();
new Thread(new producter(msg)).start();//启动生产者线程
new Thread(new Consumer(msg)).start();//启动消费者线程
}
}
class producter implements Runnable{
private Message msg;
public producter(Message msg){
this.msg=msg;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int x=0;x<100;x++)
{
if(x%2==0)
{
this.msg.setContent("社会小青年");//两个线程数据
try {
Thread.sleep(10);//增加休眠,模拟网络延迟
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.msg.setTitle("小方");//两个线程数据
}
else {
this.msg.setContent("宇宙大帅哥");//两个线程数据
try {
Thread.sleep(10);//增加休眠,模拟网络延迟
} catch (Exception e) {
// TODO: handle exception
}
this.msg.setTitle("小杨");//两个线程数据
}
}
}
}
class Consumer implements Runnable{
private Message msg;
public Consumer(Message msg) {
this.msg=msg;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int x=0;x<100;x++)
{
try {
Thread.sleep(10);//增加休眠,模拟网络延迟
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(this.msg.getTitle()+" - "+this.msg.getContent());
}
}
}
class Message{
private String title;
private String content;
public void setContent(String content) {
this.content = content;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public String getTitle() {
return title;
}
}
初次建立框架进行执行
我们发现出现了问题,在这个时候本来对应的是
- 小杨是宇宙大帅哥,小方是社会小青年
- 但是现在出现了错误的信息(数据不同步)
现在我们只是完成了程序的基本结构而并没有考虑到同步问题,接下来就需要解决同步问题
解决生产者与消费者同步问题
人如果要解决问题,首先要解决数据同步问题,如果要想解决数据同步问题最简单的就是使用同步代码块或同步方法,于是在这个时候对于同步的处理就可以直接在Message类中完成
范例:解决生产者与消费者同步问题
public class ThreadDemo{
public static void main(String[] args) throws Exception {
Message msg=new Message();
new Thread(new producter(msg)).start();//启动生产者线程
new Thread(new Consumer(msg)).start();//启动消费者线程
}
}
class producter implements Runnable{
private Message msg;
public producter(Message msg){
this.msg=msg;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int x=0;x<100;x++)
{
if(x%2==0)
{
try {
Thread.sleep(10);//增加休眠,模拟网络延迟
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.msg.set("小方","社会小青年");//两个线程数据
}
else {
try {
Thread.sleep(100);//增加休眠,模拟网络延迟
} catch (Exception e) {
// TODO: handle exception
}
this.msg.set("小杨","宇宙大帅哥");//两个线程数据
}
}
}
}
class Consumer implements Runnable{
private Message msg;
public Consumer(Message msg) {
this.msg=msg;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int x=0;x<100;x++)
{
try {
Thread.sleep(10);//增加休眠,模拟网络延迟
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(this.msg.get());
}
}
}
class Message{
private String title;
private String content;
public synchronized void set(String title,String content)
{
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.content=content;
this.title=title;
}
public synchronized String get() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return this.title+" - "+this.content;
}
}
解决完同步问题后,数据信息保持一致,但是不能避免数据重复问题
利用object类解决重复问题
如果现在想要解决生产者与消费者的问题,那么最好的解决方案就是使用等待唤醒机制,对于等待唤醒机制主要用到object类中的方法
- 等待:
|-死等:public final void wait()throws InterruptedException;//必须要有唤醒
|-设置时间等待:public final void wait(long timeout)throws InterruptedException;
|-设置等待时间:public final void wait(long timeout,int nanos)throws InterruptedException; - 唤醒第一个等待线程:public final void notify();
- 唤醒全部等待线程:public final void notifyAll();
notify()表示的是唤醒第一个等待线程,notifyAll()表示唤醒全部等待线程,那个优先级高就有可能先执行;
对于当前的问题主要的解决应该通过Message类完成处理;
范例:修改Message类
public class ThreadDemo{
public static void main(String[] args) throws Exception {
Message msg=new Message();
new Thread(new producter(msg)).start();//启动生产者线程
new Thread(new Consumer(msg)).start();//启动消费者线程
}
}
class producter implements Runnable{
private Message msg;
public producter(Message msg){
this.msg=msg;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int x=0;x<100;x++)
{
if(x%2==0)
{
try {
Thread.sleep(10);//增加休眠,模拟网络延迟
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.msg.set("小方","社会小青年");//两个线程数据
}
else {
try {
Thread.sleep(100);//增加休眠,模拟网络延迟
} catch (Exception e) {
// TODO: handle exception
}
this.msg.set("小杨","宇宙大帅哥");//两个线程数据
}
}
}
}
class Consumer implements Runnable{
private Message msg;
public Consumer(Message msg) {
this.msg=msg;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int x=0;x<100;x++)
{
try {
Thread.sleep(10);//增加休眠,模拟网络延迟
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(this.msg.get());
}
}
}
class Message{
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();
}
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.content=content;
this.title=title;
this.flag=false;//已经生产过了
super.notify();//唤醒等待的线程,有就唤醒,没有就算了
}
public synchronized String get() {
if(this.flag==true)
{
try {
super.wait();//还未生产需要等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
return this.title+" - "+this.content;
}
finally {//不管如何都要执行
this.flag=true;//继续生产
super.notify();//唤醒等待线程
}
}
}
到此,程序执行正常,生产消费一步一步进行,这种处理形式就是在进行多线程开发之中最原始的处理方案,整个等待、同步过程都是通过原生代码实现控制