生产者和消费者基本程序模型
在多线程的开发中最著名的案例就是生产者和消费者操作,该操作的主要流程如下:
生产者负责负责信息内容的生产,每当生产者生产完成一项完整的信息之后,消费者要从这里面取走信息,如果生产者没有生产则消费者要等待它生产完成,如果消费者还没有对信息进行消费,则生产者应该等待消费处理完成后再进行生产。
程序实现:
可以将生产者和消费者定义为两个独立的线程类对象,两个独立的线程之间需要有一个数据的保存集中点,那么可以单独定义一个Message类实现数据的保存。
class Message{
private String title;
private String content;
public void setTitle(String title){
this.title=title;
}
public void setContent(String content){
this.content=content;
}
public String getTitle(){
return this.title;
}
public String getContent(){
return this.content;
}
}
class Producer implements Runnable{
private Message msg;
public Producer(Message msg){
this.msg=msg;
}
@Override
public void run() {
for(int x=0;x<100;x++){
if(x%2==0){
this.msg.setTitle("产品A");
this.msg.setContent("AAAAA");
}
else{
this.msg.setTitle("产品B");
this.msg.setContent("BBBBBB");
}
}
}
}
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.getTitle()+"、"+this.msg.getContent());
}
}
}
public class ProductAndConsumer {
public static void main(String[] args) {
Message message = new Message();
new Thread(new Producer(message)).start();
new Thread(new Consumer(message)).start();
}
}
通过代码的执行会发现此时有两个主要的问题:
- 数据不同步了
- 生产一个取走一个,但是会发现有重复生产和重复取出的问题。
解决同步问题
如果想要解决问题,首先解决的就是数据同步的问题,解决数据同步最简单的做法是使用synchronized关键字定义同步代码块或同步方法,于是这个时候对于同步的处理就可以直接再Message类中完成。
class Message{
private String title;
private String content;
public synchronized void set(String title,String content) throws InterruptedException {
this.title=title;
Thread.sleep(100);
this.content=content;
}
public synchronized String get() throws InterruptedException {
Thread.sleep(10);
return this.title+"-"+this.content;
}
}
class Producer implements Runnable{
private Message msg;
public Producer(Message msg){
this.msg=msg;
}
@Override
public void run() {
for(int x=0;x<100;x++){
if(x%2==0){
try {
this.msg.set("产品A","AAAAA");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{
try {
this.msg.set("产品B","BBBBB");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
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 {
System.out.println(this.msg.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ProductAndConsumer {
public static void main(String[] args) {
Message message = new Message();
new Thread(new Producer(message)).start();
new Thread(new Consumer(message)).start();
}
}
在进行同步处理的时候肯定需要一个同步的处理对象,那么此时肯定要将同步操作交由Message类处理是最合适的。这个时候数据已经可以正常的保持一致了,但是对于重复操作的问题依然存在。
利用Object类解决重复操作
如果想要解决生产者和消费者问题,那么最好的解决方案就是使用等待与唤醒机制,而对于等待与唤醒的造作机制,主要依靠的是Object类中提供的方法处理的。
-
等待机制
//死等 public final void wait() throws InterruptedException //设置等待时间 public final void wait(long timeout) throws InterruptedException //设hi在等待时间 public final void wait(long timeout, int nanos) throws InterruptedException
-
唤醒第一个等待线程:
public final void notify()
-
唤醒全部等待线程:
//唤醒全部的线程,哪个线程的优先级高就有可能先执行 public final void notifyAll()
对于当前问题的解决应该通过Message类完成处理
class Message{
private String title;
private String content;
/**
* 表示生产或消费的形式
* flag=true 允许生产 不允许消费
* flag=false 允许消费 不允许生产
*/
private boolean flag=true;
public synchronized void set(String title,String content) throws InterruptedException {
if(!this.flag){//无法进行生产,应该等待被消费
super.wait();
}
this.title=title;
Thread.sleep(100);
this.content=content;
this.flag=false;//已经生产过啦
super.notify();//唤醒等待的线程
}
public synchronized String get() throws InterruptedException {
if(this.flag){//还未生产 需要等待
super.wait();
}
Thread.sleep(10);
try{
return this.title+"-"+this.content;
}finally{ //不管如何都要执行
this.flag=true;//可以继续进行生产
super.notify(); //唤醒等待线程
}
}
}
class Producer implements Runnable{
private Message msg;
public Producer(Message msg){
this.msg=msg;
}
@Override
public void run() {
for(int x=0;x<100;x++){
if(x%2==0){
try {
this.msg.set("产品A","AAAAA");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{
try {
this.msg.set("产品B","BBBBB");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
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 {
System.out.println(this.msg.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ProductAndConsumer {
public static void main(String[] args) {
Message message = new Message();
new Thread(new Producer(message)).start();
new Thread(new Consumer(message)).start();
}
}
这种处理形式就是在多线程开发中最原始的处理方案,整个的等待、同步唤醒机制都是开发者自行通过 原生代码实现控制。