生产者与消费者的操作
在多线程的开发过程之中最为著名的案例就是生产者与消费者操作,该操作的主要流程如下:
‒ 生产者负责信息内容的生产
‒ 每当生产者生产完成一项完整的信息之后消费者要从这里取走信息
‒ 如果生产者没有生产完则消费者要等待它完成,如果消费者还没有对信息进行消费,则生产者应该等待消费处理完成后再继续生产
可以将生产者与消费者定义为两个独立的线程类对象,但是对于现在生产的数据,可以使用如下的组成
‒ 数据一:title = 小王,content = 宇宙大帅哥
‒ 数据二:title = 小高,content = 猥琐第一人
既然生产者与消费者是两个独立的线程,那么这两个独立的线程之间就需要有一个数据的保存集中点,那么可以单独定义一个Message类实现数据保存。
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 this.content ;
}
public String getTitle(){
return this.title;
}
}
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("小王");
this.msg.setContent("宇宙大帅哥");
}else{
this.msg.setTitle("小高");
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.getTitle() + "- " + this.msg.getContent());
}
}
}
Message msg = new Message() ;
new Thread(new Producer(msg)).start(); // 启动生产者线程
new Thread(new Consumer(msg)).start();//启动消费者线程
通过整个代码的执行会发现此时有两个主要问题:
‒ 问题一:数据不同步
‒ 问题二:生产一个取走一个,但是发现有重复生产和重复取出问题
程序开发先考虑基础模型,然后依据基础模型进行改变,很难保证一次性解决问题,这是不可能的
项目代码都是需要分析和设计的。
同步问题的解决
如果要解决问题,首先要解决的就是数据同步的处理问题,如果要想解决数据同步最简单的做法是使用synchronized定义代码块或同步方法,于是这个时候对于同步的处理就可以直接在Message类中完成。
class Message{
private String title ;
private String content;
public synchronized void set(String title,String content){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
this.title = title;
this.content = content ;
}
public String get(){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return this.title +" - " + this.content ;
}
}
在进行同步处理的时候需要有一个同步的处理对象,那么此时肯定要将同步操作交给Message类处理是最合适的。这个时候发现数据已经可以保持正常的保持一致了,但是对于重复操作的问题依然存在。
利用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类完成处理.
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){
e.printStackTrace();
}
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();;
}
this.title = title;
this.content = content ;
this.flag = false ; // 已经生产过了
super.notify(); // 唤醒等待的线程
}
public synchronized String get(){
if(this.flag == true){ // 还未生产,需要等待
try {
super.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try{
return this.title +" - " + this.content ;
}finally{
this.flag = true ; //继续生产
super.notify(); //唤醒等待线程
}
}
}
这种处理形式就是在进行多线程开发过程之中最原始的处理方案,整个的等待,同步,唤醒机制都由开发者自行通过原生代码实现控制。