同一ExecutorService中的多个生产者和单个使用者

时间:2016-06-13 17:23:39

标签: java multithreading

问题陈述:从无限的整数流中识别两个连续的整数,其中这些整数由多个生产者生成,但单个消费者在再次重复相同的数字时会发出警报。

我有多个Producers和一个Consumer。如果我将消费者提交给同一ExecutorService,消费者就没有开始。但是如果我在单独的Thread中运行Consumer,则Consumer线程会按预期启动。

代码:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.locks.ReentrantLock;
import java.util.Iterator;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;

public class FixedBlockingQueue {
    final BlockingQueue<Integer> queue;
    private int capacity;

    public FixedBlockingQueue(int capacity){
        super();
        this.capacity = capacity;
        queue = new ArrayBlockingQueue<Integer>(capacity);
        System.out.println("Capactiy:"+this.capacity);
    }
    public void addElement(Integer element){
        try{
            queue.put(element);
        }catch(Exception err){
            err.printStackTrace();
        }
    }
    public void startThreads(){
        ExecutorService es = Executors.newFixedThreadPool(1);
        for ( int i =0; i < 10; i++){
            es.submit(new MyProducer(this));
        }
        //es.submit(new MyConsumer(queue));
        new Thread(new MyConsumer(this)).start();
    }
    public BlockingQueue<Integer> getQueue(){
        return queue;
    }
    public static void main(String args[]){
        FixedBlockingQueue f = new FixedBlockingQueue(1);
        f.startThreads();
    }
}

class MyProducer implements Runnable{

    private FixedBlockingQueue queue;
    public MyProducer(FixedBlockingQueue queue){
        this.queue = queue;     
    }
    public void run(){
        for ( int i=1; i< 5; i++){
            queue.addElement(new Integer(i));
            System.out.println("adding:"+i);
        }
    }
}

class MyConsumer implements Runnable{
    private BlockingQueue<Integer>  queue;
    Integer firstNumber = 0;
    private final ReentrantLock lock = new ReentrantLock();

    public MyConsumer(FixedBlockingQueue fQueue){
        this.queue = fQueue.getQueue();
    }
    /* TODO : Compare two consecutive integers in queue are same or not*/
    public void run(){  
        Integer secondNumber = 0;
        while ( true){
            try{
                lock.lock();
                System.out.println("queue size:"+queue.size());
                if ( queue.size() > 0) {
                    secondNumber = queue.remove();
                    System.out.println("Removed:"+secondNumber);
                    System.out.println("Numbers:Num1:Num2:"+firstNumber+":"+secondNumber);
                    if ( firstNumber.intValue() ==  secondNumber.intValue()){
                        System.out.println("Numbers matched:"+firstNumber);
                    }
                    firstNumber = secondNumber;
                }
                Thread.sleep(1000);
            }catch(Exception err){
                err.printStackTrace();
            }finally{
                lock.unlock();
            }
        }
    }
}

输出:

Capactiy:1
adding:1

如果我从

更改代码
es.submit(new MyConsumer(queue));
//new Thread(new MyConsumer(queue)).start();

//es.submit(new MyConsumer(queue));
new Thread(new MyConsumer(queue)).start();

消费者线程正常启动。

输出:

Capactiy:1
adding:1
queue size:1
Removed:1
Numbers:Num1:Num2:0:1
adding:2
queue size:1
Removed:2
Numbers:Num1:Num2:1:2
adding:3
queue size:1
Removed:3
Numbers:Num1:Num2:2:3
adding:4
queue size:1
Removed:4
Numbers:Num1:Num2:3:4
adding:1
queue size:1
Removed:1
Numbers:Num1:Num2:4:1
adding:2
queue size:1
Removed:2
adding:3
Numbers:Num1:Num2:1:2
queue size:1
Removed:3
Numbers:Num1:Num2:2:3

第一种方法:

我知道消费者不会使用该号码,但理想情况下不应阻止提交其他Producer任务。

如果是这种情况,使用ExecutorService代替简单Threads无法100%实现?

1 个答案:

答案 0 :(得分:2)

您创建一个具有单个线程和一个具有固定容量的BlockingQueue的线程池。然后您向池中提交三个任务:前两个每个尝试将五个值排入队列,然后一个使值出列何时可用。

因为您的固定大小的池只有一个线程,所以您提交给它的任务将按顺序运行,而不是并行运行。您首先提交生产者任务,因此它首先运行。但是一旦它将第一个数字排队,它就无法取得任何进一步的进展,因为队列已满。并且队列将保持永久满,因为生成器任务必须在池化线程可用于其他任务(例如消费者)之前完成。

我不确定你为什么要使用线程池,因为直接进行线程管理并不困难,特别是因为你的任务已经实现了Runnable。但是,如果你使用了一个池,那么确保它有足够的线程来同时容纳所有任务。

另请注意,BlockingQueue实现应该是线程安全的,标准库提供的所有实现都是如此。因此,您无需在addElement()中执行自己的锁定。此外,如果你确实需要执行自己的锁定,那么你不仅需要在将元素排队时,而且在将它们出列时也要这样做。

此外,如果您的生产者任务通过FixedBlockingQueue实例间接地向底层队列添加元素,那就超出了奇怪的程度,但是让消费者任务直接进入底层队列。

并且您的FixedBlockingQueue类的名称选择不当,因为它意味着该类实现了BlockingQueue,但该类实际上并没有这样做。