------ android培训、java培训、期待与您交流! ----------
线程操作中有一个经典程序,就是生产者与消费者。
生产者生产出信息后将其放到一个区域中,消费者从中取出数据。由于涉及到线程运行的不稳定性,会存在以下两个问题:
1: 生产者线程刚向数据的存储空间添加了信息名称,还没有加入该信息的内容,程序就切换到消费者线程,消费者线程将信息的名称和上一个信息的内容联系到一起了。
2:生产者放了无数次数据,消费者才开始取数据,或者消费者取完一个数据,还没等到生产者放入新的数据,又重复取出已有数据。
代码如下:
class Info{ // 定义信息类
private String name ; // 定义name属性
private String content; // 定义content属性
public void setName(String name){
this.name = name ;
}
public void setContent(String content){
this.content = content ;
}
public String getName(){
return this.name ;
}
public String getContent(){
return this.content ;
}
};
class Producer implements Runnable{ // 通过Runnable实现多线程
private Info info = null ; // 保存Info引用
public Producer(Info info){
this.info = info ;
}
public void run(){
boolean flag = false ; // 定义标记位
for(int i=0;i<50;i++){
if(flag){
this.info.setName("苹果iPhone") ; // 设置名称
try{
Thread.sleep(90) ;
}catch(InterruptedException e){
e.printStackTrace() ;
}
this.info.setContent("手机") ; // 设置内容
flag = false ;
}else{
this.info.setName("索尼walkman") ; // 设置名称
try{
Thread.sleep(90) ;
}catch(InterruptedException e){
e.printStackTrace() ;
}
this.info.setContent("随身听") ; // 设置内容
flag = true ;
}
}
}
};
class Consumer implements Runnable{
private Info info = null ;
public Consumer(Info info){
this.info = info ;
}
public void run(){
for(int i=0;i<50;i++){
try{
Thread.sleep(90) ;
}catch(InterruptedException e){
e.printStackTrace() ;
}
System.out.println(this.info.getName() +
" --> " + this.info.getContent()) ;
}
}
};
public class ThreadCaseDemo01{
public static void main(String args[]){
Info info = new Info(); // 实例化Info对象
Producer pro = new Producer(info) ; // 生产者
Consumer con = new Consumer(info) ; // 消费者
new Thread(pro).start() ;
new Thread(con).start() ;
}
};
可以看到,两个问题已经出现。首先用同步来解决:
在class info中,可以将设置名称和姓名定义成一个同步方法:
class Info{ // 定义信息类
private String name; // 定义name属性
private String content ; // 定义content属性
public synchronized void set(String name,String content){
this.setName(name) ; // 设置名称
try{
Thread.sleep(300) ;
}catch(InterruptedException e){
e.printStackTrace() ;
}
this.setContent(content) ; // 设置内容
}
public synchronized void get(){
try{
Thread.sleep(300) ;
}catch(InterruptedException e){
e.printStackTrace() ;
}
System.out.println(this.getName() +
" --> " + this.getContent()) ;
}
public void setName(String name){
this.name = name ;
}
public void setContent(String content){
this.content = content ;
}
public String getName(){
return this.name ;
}
public String getContent(){
return this.content ;
}
};
该类中,设置名称和内容用一个同步来实现,取得名称和内容也用一个同步实现;
相应也要修改Producer和Consumer;
class Producer implements Runnable{ // 通过Runnable实现多线程
private Info info = null ; // 保存Info引用
public Producer(Info info){
this.info = info ;
}
public void run(){
boolean flag = false ; // 定义标记位
for(int i=0;i<50;i++){
if(flag){
this.info.set("苹果iPhone","手机") ; // 设置名称
flag = false ;
}else{
this.info.set("索尼walkman","随身听") ; // 设置名称
flag = true ;
}
}
}
};
class Consumer implements Runnable{
private Info info = null ;
public Consumer(Info info){
this.info = info ;
}
public void run(){
for(int i=0;i<50;i++){
this.info.get() ;
}
}
};
程序运行结果:
第一个问题解决。但在运行中可以发现每次启动时,只有一个生产者启动,即消费者重复生产。第二个生产者始终未启动。
生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者,通常采用线程间通信的方法解决该问题。如果解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。
如果想让生产者不重复生产,消费者不重复取走,可以增加一个标志位,并假设标志位是布尔型变量,当标志位为true,则表示可以生产,不能取走。反之,标志位是false,表示可以取走,不能生产。
为了完成上述功能,我在info类中加入标志位,这样直接修改info类即可,不必修改生产者和消费者:代码如下:
class Info{ // 定义信息类
private String name; // 定义name属性
private String content; // 定义content属性
private int count=1;
//private int count2=1;
private boolean flag = false ; // 设置标志位
public synchronized void get(){
while(flag){
try{
super.wait() ;
}catch(InterruptedException e){
e.printStackTrace() ;
}
}
/*try{
Thread.sleep(300) ;
}catch(InterruptedException e){
e.printStackTrace() ;
}*/
System.out.println("Consumer..."+this.getName() + " --> " + this.getContent()+".....") ;
flag = true ; // 改变标志位,表示可以生产
this.notifyAll() ;
}
public synchronized void set(String name,String content){
while(!flag){
try{
super.wait() ;
}catch(InterruptedException e){
e.printStackTrace() ;
}
}
this.setName(name); // 设置名称
/*try{
Thread.sleep(300) ;
}catch(InterruptedException e){
e.printStackTrace() ;
}*/
this.setContent(content) ; // 设置内容
System.out.println("Producter..."+this.getName() + " --> " + this.getContent()+".....");
flag = false ; // 改变标志位,表示可以取走
this.notifyAll() ;
}
public void setName(String name){
this.name = name ;
}
public void setContent(String content){
this.content = content+"-->"+count++ ;
}
public String getName(){
return this.name ;
}
public String getContent(){
return this.content ;
}
};
运行结果:
问题解决。