在《一个简单的生产者与消费者的多线程例子(一)》给出了两种具备雏形的生产者与消费者的多线程模式。但是在运行过程中,有一个问题:算法不会自动停止,即使没有products了,consumer还会一直无限期地Wait下去。因此,需要解决阻止消费者无限期地等待的问题。
问题解决思路:当生产者不再生产时,利用一个状态变量通知channel,消费者检测这个状态变量,当发现生产者不再生产后,且channel中没有products了,就return(不要用System.exit(m),因为这会把其他的运行也都终止掉!)。
分析:因为这个状态变量要同时被生产者和使用者使用,因此,放到channel中。
设置:取这个变量为:boolean produceOff,当值为false时表示生产者还在生产,否则生产者停止生产了。
代码工作:
- 修改channel中的take()方法,加入count和produceOff的联合状态判断;
- 修改consumer的consum()方法,加入produceOff的状态判断;
- 修改producer的produce()方法,使其生产有限个products
- 构建一个market,管理对应的channel,producer和consumer
代码如下:
Channel
public class MyChannel4ThreadStop implements MyChannel{
private final MyProduct[] queue;
private int tail, head, count;
private boolean produceOff = false;
public MyChannel4ThreadStop(int size1) {
queue = new MyProduct[size1];
this.head = 0;
this.tail = 0;
this.count = 0;
}
@Override
public synchronized void put(MyProduct e) {
try {
while (count >= queue.length) {
wait();
//System.out.println(Thread.currentThread().getName()+"put() is waiting");
}
queue[count] = e;
tail = (tail + 1) % queue.length;
count++;
System.out.println(Thread.currentThread().getName() + "Put in one.Now the number of the products in the channel is:" + count);
notifyAll();
} catch (InterruptedException exception) {
exception.printStackTrace();
}
}
@Override
public synchronized MyProduct take() {//Consumer will stop according to the null return value
MyProduct e = null;
try {
while (count <= 0 && !produceOff) {
wait();
//System.out.println(Thread.currentThread().getName()+"take() is waiting!");
}
if (count > 0) {
e = queue[head];
count--;
System.out.println(Thread.currentThread().getName() + "Take one out.Now the number of the products in the channel is:" + count);
notifyAll();
}else if(produceOff&&count<=0){
e=null;
}
} catch (InterruptedException exception) {
exception.printStackTrace();
}
return e;
}
/**
* @param produceOff the produceOff to set
*/
public void setProduceOff(boolean produceOff) {
this.produceOff = produceOff;
}
}
Producer
public class ASimpleProducer4ThreaStop {
private final MyChannel4ThreadStop pChannel;
public ASimpleProducer4ThreaStop(MyChannel4ThreadStop pChannel1){
pChannel=pChannel1;
}
public void produce() {
for (int i = 0;i<50; i++) {
ASimpleProduct product = new ASimpleProduct("" + i);
pChannel.put(product);
try {
Thread.sleep(i);
} catch (InterruptedException ex) {
Logger.getLogger(MyMarketSizeUnlimited.class.getName()).log(Level.SEVERE, null, ex);
}
}
pChannel.setProduceOff(true);
}
}
Consumer
public class ASimpleConsummer4ThreadStop extends MyConsumer{
public ASimpleConsummer4ThreadStop(MyChannel myChannel1) {
super(myChannel1);
}
@Override
public void run() {
try {
while (true) {
Thread.sleep(50);
MyProduct product = myChannel.take();
if (null == product) {
return;
} else {
consum(product);
}
}
} catch (InterruptedException ex) {
Logger.getLogger(ASimpleConsumer.class.getName()).log(Level.SEVERE, null, ex);
}
}
@Override
protected void consum(MyProduct product) {
}
}
Market
public class MyMarket4ThreadStop {
public static void main(String[] args) {
MyChannel4ThreadStop mychannel = new MyChannel4ThreadStop(20);
for (int i = 0; i < 5; i++) {
new Thread(new ASimpleConsummer4ThreadStop(mychannel)).start();
}
new ASimpleProducer4ThreaStop(mychannel).produce();
}
}
上述例子中,生产者不是线程,那么如果生产者也是线程,那么不仅要考虑不让消费者无限期等待,还需要考虑不让生产者无限期等待?这样就要两个问题需要解决。
好消息是,上个例子中解决了第一个问题:不让消费者无限期等待。坏消息是:还需要解决第二个问题:不让生产者无限期等待。
思路:如果生产者是Thread,那么,在produce任务完成后,直接调用自己的interrupt()即可。如果producer是Runnable的implements,那么就需要获得包装它的Thread,而这个Thread在Market中被包装,因此,从Market入手。只需要修改上述例子中Producer和Market的代码即可。
Producer的代码
public class ASimpleProducer4PCstop extends MyProducer {
private Thread thisThread;
public ASimpleProducer4PCstop(MyChannel myChannel1) {
super(myChannel1);
}
@Override
public void run() {
this.make();
}
@Override
protected void make() {
for (int i = 0; i < 50; i++) {
ASimpleProduct product = new ASimpleProduct("" + i);
this.myChannel.put(product);
try {
Thread.sleep(i);
} catch (InterruptedException ex) {
Logger.getLogger(ASimpleProducer4PCstop.class.getName()).log(Level.SEVERE, null, ex);
}
}
((MyChannel4ThreadStop) myChannel).setProduceOff(true);
System.out.println("The interrupt will be called!");
thisThread.interrupt();
}
/**
* @param thisThread the thisThread to set
*/
public void setThisThread(Thread thisThread) {
this.thisThread = thisThread;
}
}
Market的代码
public class MyMarket4ConsumerProducerStop {
public static void main(String[] args) {
MyChannel4ThreadStop mychannel = new MyChannel4ThreadStop(20);
for (int i = 0; i < 5; i++) {
new Thread(new ASimpleConsummer4ThreadStop(mychannel)).start();
}
for (int i = 0; i < 5; i++) {
ASimpleProducer4PCstop asppc = new ASimpleProducer4PCstop(mychannel);
Thread t = new Thread(asppc);
t.start();
asppc.setThisThread(t);
}
}
}
注意到,market代码中把包装producer的Runnable的Thread传递给了producer,供他自己停止自己。
上述两个例子有一个假设是:程序结束的权限在生产者,即当生产者不再生产时,发一个状态变量给channel,消费者根据这个状态变量的值结束程序。