多线程编程:生产者与消费者基本程序模型
发布日期:2021-05-07 09:11:21 浏览次数:25 分类:精选文章

本文共 5477 字,大约阅读时间需要 18 分钟。

生产者与消费者基本程序模型

在多线程的开发过程之中最为著名的案例就是生产者与消费者,该操作主要的流程如下:

  • 生产者负责信息内容的生产
  • 每当生产者生产完成一项完整的信息之后消费者要从这里面取走信息
  • 如果生产者没有生产完则消费者要等待它生产完成,反之如果消费者还没有对信息进行消费,则生产者应该等待消费处理完成后再继续生产。

程序的基本实现

可以将生产者与消费者定义为两个独立的线程类对象,但是对于现在生产的数据可以使用如下的数据组成:

  • 数据一:title = 小王、content = 高富帅
  • 数据而:title =小周、 content =矮穷矬

既然生产者与消费者是两个独立的线程,那么这两个独立的线程之间就需要有一个数据的保存的集中点,这个点取名为Message。

package multithreading;public class ProducersAndConsumers {	public static void main(String[] args) {		Message msg = new Message();		new Thread(new Producer(msg)).start();//启动生产者线程		new Thread(new Consumer(msg)).start();//启动消费者线程	}}class Message{	private String title;	private String content;	//Steer、getter	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;	}}class Producer implements Runnable{	private Message msg;	public Producer(Message msg) {//构造方法获取Message对象		this.msg = msg;	}	@Override	//覆写run()方法	public void run() {		for (int i = 0; i < 100; i++) {			if(i % 2 == 0) {				//增加延迟				try {					Thread.sleep(10);				} catch (InterruptedException e) {					// TODO 自动生成的 catch 块					e.printStackTrace();				}				this.msg.setContent("高富帅");				this.msg.setTitle("小王");			}else {				//增加延迟				try {					Thread.sleep(10);				} catch (InterruptedException e) {					// TODO 自动生成的 catch 块					e.printStackTrace();				}				this.msg.setContent("矮穷矬");				this.msg.setTitle("小周");			}					}	}}class Consumer implements Runnable{	private Message msg;	public Consumer(Message msg) {		this.msg = msg;	}	@Override	public void run() {		for(int x = 0;x < 100;x++) {			//增加延迟			try {				Thread.sleep(10);			} catch (InterruptedException e) {				// TODO 自动生成的 catch 块				e.printStackTrace();			}			System.out.println(this.msg.getTitle()+" - "+this.msg.getContent());		}	}	}

通过整个代码的执行可以发现有两个主要问题:

  • 数据不同步
  • 本质应该是生产一个取走一个,但是发现有了重复生产和重复取出的问题

解决同步问题

解决数据同步最简单的方法是synchronizes关键字定义同步代码块或方法。

package multithreading;public class ProducersAndConsumers {	public static void main(String[] args) {		Message msg = new Message();		new Thread(new Producer(msg)).start();//启动生产者线程		new Thread(new Consumer(msg)).start();//启动消费者线程	}}class Message{	private String title;	private String content;	//Steer、getter	public synchronized void setContent(String content,String title) {		this.content = content;		//增加延迟		try {			Thread.sleep(10);		} catch (InterruptedException e) {			e.printStackTrace();		}		this.title = title;	}	public synchronized String getContent() {		//增加延迟		try {			Thread.sleep(10);		} catch (InterruptedException e) {			e.printStackTrace();		}		return this.content + "---"+this.title;	}}class Producer implements Runnable{	private Message msg;	public Producer(Message msg) {//构造方法获取Message对象		this.msg = msg;	}	@Override	//覆写run()方法	public void run() {		for (int i = 0; i < 100; i++) {			if(i % 2 == 0) {					this.msg.setContent("高富帅","小王");			}else {						this.msg.setContent("矮穷矬","小周");			}					}	}}class Consumer implements Runnable{	private Message msg;	public Consumer(Message msg) {		this.msg = msg;	}	@Override	public void run() {		for(int x = 0;x < 100;x++) {			System.out.println(this.msg.getContent());		}	}	}

此时已经结局了数据不同步的问题,接下来解决重复生产与消费的问题

线程等待与唤醒

Message类中控制生产者只生产一商品,直到消费者取走再生产。这就是线程等待与唤醒,主要有Object类中的方法进行处理。

  • 等待机制:
  • 死等:public final void wait() throws InterruptedException
  • 设置等待时间:public final void wait(long timeout) throes InterruptedException
  • 设置等待时间:public final void wait(long timeout,int nanos) throes InterruptedException
  • 唤醒第一个等待线程:public final void notify()
  • 唤醒全部等待线程:public final void notifyAll()
  • 此时如果有若干个线程的话,那么notify()表示的是第一个等待的,其他线程继续等,notifyAll表示会唤醒所有的等待线程,哪个线程的优先级高,就有可能先执行。

 

范例:修改Message类

package multithreading;public class ProducersAndConsumers {	public static void main(String[] args) {		Message msg = new Message();		new Thread(new Producer(msg)).start();//启动生产者线程		new Thread(new Consumer(msg)).start();//启动消费者线程	}}class Message{	private String title;	private String content;	private boolean flag = true;//表示生产或消费的形式	//flag = true表示允许生产,此时消费者等待。false时不允许生产,直到产品被消费	//Steer、getter	public synchronized void setContent(String content,String title) {		if(this.flag ==  false) {//无法进行生产,应该等待被消费			try {				super.wait();			} catch (InterruptedException e) {				e.printStackTrace();			}		}		this.content = content;		//增加延迟		try {			Thread.sleep(10);		} catch (InterruptedException e) {			e.printStackTrace();		}		this.title = title;		this.flag = false;//已经生产过了		super.notify();//唤醒等待的线程	}	public synchronized String getContent() {		if(this.flag ==  true) {//还未生产,需要等待			try {				super.wait();			} catch (InterruptedException e) {				e.printStackTrace();			}		}		//增加延迟		try {			Thread.sleep(10);		} catch (InterruptedException e) {			e.printStackTrace();		}		try {			return this.content + "---"+this.title;		} finally {	//不管如何都要执行			this.flag = true;//继续生产			super.notify();//唤醒等待的线程		}			}}class Producer implements Runnable{	private Message msg;	public Producer(Message msg) {//构造方法获取Message对象		this.msg = msg;	}	@Override	//覆写run()方法	public void run() {		for (int i = 0; i < 100; i++) {			if(i % 2 == 0) {					this.msg.setContent("高富帅","小王");			}else {						this.msg.setContent("矮穷矬","小周");			}					} 	}}class Consumer implements Runnable{	private Message msg;	public Consumer(Message msg) {		this.msg = msg;	}	@Override	public void run() {		for(int x = 0;x < 100;x++) {			System.out.println(this.msg.getContent());		}	}	}

这是多线程开发中最原始的处理方案,整个等待、同步、唤醒机制都由开发者通过原生代码实现控制

上一篇:优雅的停止线程
下一篇:线程同步处理

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2025年03月30日 19时59分10秒