简单实现生产者、消费者模型

来源:互联网 发布:最好的期货软件 知乎 编辑:程序博客网 时间:2024/05/01 12:41

闲来无事,写了段生产者/消费者的代码。写的过程中遇到了一些问题,有些地方着实费了一番周折,特写此文以备后忘。

1.代码层次

实现中定义了四个类分别是comm、ReadPort、WritePort、SerialBuffer。

class comm:<main-class >

public class Comm {

public static void main(String[] args) {

SerialBuffer sb
=new SerialBuffer();
  ReadPort rp0
=new ReadPort(sb);
  ReadPort rp
=new ReadPort(sb);
  WritePort wp
=new WritePort(sb);
  WritePort wp1
=new WritePort(sb);
  rp0.start();
  wp.start();
  wp1.start();
  rp.start( );
 }


}


class ReadPort:

function: 向临界区加入数据

 

class ReadPort extends Thread{
 
private SerialBuffer serialBuffer;
 
private boolean isStop=false;
 
private byte[] b=new byte[]{0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x20,0x21,0x22};
 
public ReadPort(SerialBuffer sb){
  serialBuffer
=sb;
 }

 
public void run(){
  
while(!isStop){
   
try{
    sleep(
2000);
   }
catch(InterruptedException e){}
   
   
for(int i=1;i<50;i++){
    serialBuffer.addInBuffer(b);
   }

   isStop
=true;
  }

 }

}

 

 class WritePort:

function:从临界区取出数据

class WritePort extends Thread{
 SerialPort serialPort;
 OutputStream os;
 
private boolean isStop=false;
 
private SerialBuffer serialBuffer;
 
public WritePort(SerialBuffer sb){
  serialBuffer
=sb;
 }

 
public void run(){
  
while(!isStop){
   
try{
    sleep(
2000);
   }
catch(InterruptedException e){}
   serialBuffer.delFromBuffer();
  }

 }

}


 

class SerialBuffer:

function:临界区存储数据

class SerialBuffer{
 
//默认size 和递增size
 public static int SERIALBUFFER_SIZE=24;
 
public static int SERIALBUFFER_INCREMENT=512;
 
private ByteBuffer b;

 
private boolean debug=true;
 
 
public SerialBuffer(){
  b
=ByteBuffer.allocateDirect(SERIALBUFFER_SIZE);
  
 }

 
public synchronized void addInBuffer(byte[] _in){

  System.out.println(
" ====== "+Thread.currentThread().getName()+" 第"+(curNum++)+"次写入 ======");


  
  
while(b.position()==b.capacity()||(b.position()+_in.length>b.capacity())){

   
try{
    System.out.println(
"当前缓存区已满,不能再加入数据,暂时休眠.");
    
     
    
this.wait();
    
   }
catch(InterruptedException e){
    System.out.println(
"异常:当前缓存区已满,暂时休眠.");
   }

  }

  System.out.println(
"加入"+_in.length+"个字节");
  b.put(_in);
  notify();
 }

 
 
public synchronized byte[] delFromBuffer(){
  System.out.print(
" "+Thread.currentThread().getName()+" 开始取数据");
//flip();  

//while(b.limit()==0){

for(b.flip();b.limit()==0;b.flip()){//这里 返回到下文

   
try{
    System.out.println(
"当前无数据可取,暂时休眠.");
    b.clear();
    
this.wait();
   }
catch(InterruptedException e){
    System.out.println(
"异常:当前无数据可取,暂时休眠.");
   }

   
  }

  System.out.println(Thread.currentThread().getName()
+" 可以取数据。。。");
  
byte[] rebyte=new byte[b.limit()];
  System.out.println(
"取出"+rebyte.length+"个字节");
  b.get(rebyte);
  b.clear();
  notify();
  
  
return rebyte;
 }

}


2.临界区处理

临界区使用了java nio中的byteBuffer作为数据缓存,第一次使用byteBuffer遇到了些小问题,在这里原本用flip();+while(conditions)

在一个生产者、一个消费者时没有问题,但是调整到多个生产者和多个消费者时,会抛出BufferUnderflowException异常

3.运行结果

设置1个生产者/2个消费者时的运行结果:


Thread-1 开始取数据当前无数据可取,暂时休眠.

Thread-2 开始取数据当前无数据可取,暂时休眠.

====== Thread-0 第1次写入 ======
加入12个字节
Thread-1 可以取数据。。。
取出12个字节
当前无数据可取,暂时休眠.

====== Thread-0 第2次写入 ======
加入12个字节

====== Thread-0 第3次写入 ======
加入12个字节

====== Thread-0 第4次写入 ======
当前缓存区已满,不能再加入数据,暂时休眠.
Thread-2 可以取数据。。。
取出24个字节
加入12个字节

====== Thread-0 第5次写入 ======
加入12个字节

====== Thread-0 第6次写入 ======
当前缓存区已满,不能再加入数据,暂时休眠.

Thread-1 开始取数据Thread-1 可以取数据。。。
取出24个字节
加入12个字节

====== Thread-0 第7次写入 ======
加入12个字节

====== Thread-0 第8次写入 ======
当前缓存区已满,不能再加入数据,暂时休眠.

Thread-2 开始取数据Thread-2 可以取数据。。。
取出24个字节
加入12个字节

====== Thread-0 第9次写入 ======
加入12个字节

====== Thread-0 第10次写入 ======
当前缓存区已满,不能再加入数据,暂时休眠.

Thread-1 开始取数据Thread-1 可以取数据。。。
取出24个字节
加入12个字节

====== Thread-0 第11次写入 ======
加入12个字节

====== Thread-0 第12次写入 ======
当前缓存区已满,不能再加入数据,暂时休眠.

Thread-2 开始取数据Thread-2 可以取数据。。。
取出24个字节
加入12个字节

====== Thread-0 第13次写入 ======
加入12个字节

====== Thread-0 第14次写入 ======
当前缓存区已满,不能再加入数据,暂时休眠.

Thread-1 开始取数据Thread-1 可以取数据。。。
取出24个字节

Thread-2 开始取数据当前无数据可取,暂时休眠.
加入12个字节

====== Thread-0 第15次写入 ======
加入12个字节

====== Thread-0 第16次写入 ======
当前缓存区已满,不能再加入数据,暂时休眠.
Thread-2 可以取数据。。。
取出24个字节
加入12个字节

====== Thread-0 第17次写入 ======
加入12个字节

====== Thread-0 第18次写入 ======
当前缓存区已满,不能再加入数据,暂时休眠.

Thread-1 开始取数据Thread-1 可以取数据。。。
取出24个字节
加入12个字节

====== Thread-0 第19次写入 ======
加入12个字节

Thread-2 开始取数据Thread-2 可以取数据。。。
取出24个字节

Thread-1 开始取数据当前无数据可取,暂时休眠.

Thread-2 开始取数据当前无数据可取,暂时休眠.

小结

对于多个生产者和多个消费者访问临界区时产生异常的问题,我分析产生的原因如下:1.首先要明确byteBuffer在读数据前需要调flip()方法,从而对position和limit以及remaining等值进行设置。2.是当取数据线程经过一次wait,再次由runnable转换为run状态时,由于当前运行环境可能已经发生了变化(即不同于此线程发生wait时的环境),所以需要重新flip()。但是flip()方法此时却访问不到,(在while(conditions)之外!)所以没有经过flip()直接get()就可能会抛出异常。

 

原创粉丝点击