线程池调用阻塞方法

时间:2016-07-12 14:38:20

标签: java multithreading concurrency queue threadpool

我已经实现了一个存储类似于BlockingQueue的元素的队列。在检索时,消费者可以指定针对队列元素测试的Predicate。队列将以FIFO样式返回元素,但跳过所有不满足此谓词的元素。因此返回的元素可能不是队列的头部。如果没有队列元素满足给定的谓词,take() - 线程会休眠一段时间并重新开始。

有一些线程向此队列添加元素,现在我需要许多线程来消耗该队列中的元素。

添加元素很简单。但是,如何将此队列“连接”到工作池(最好是具有动态线程管理的ThreadPoolExecutor),从这个队列中检索元素并做一些工作?

我的队列有两种方法:

boolean add(E e);
E take(); // blocks

队列实现基本上类似于Condition中的示例,除了它是无界的并且没有数组支持,但是LinkedHashSet不允许重复并维护插入顺序。

我想出了这个,但我不知道这是否是要走的路。我真的需要这个额外的线程吗?

SynchronousQueue<Runnable> workQ = new SynchronousQueue<>();
ExecutorService threadPool = new ThreadPoolExecutor(10, 100, 30L, TimeUnit.SECONDS, workQ);
new Thread(() -> {
    try {
        while (true) {
            workQ.put(() -> process(queue.take()));
        }
    } catch (InterruptedException e) {
    }
}).start();

1 个答案:

答案 0 :(得分:1)

这个问题背后的问题是:如何将ThreadPoolExecutor与包含非Runnable元素的队列结合使用?例如:

BlockingQueue<String> strings = new LinkedBlockingQueue<>();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 100, 30L, TimeUnit.SECONDS, strings);

(我知道在这个例子中,线程池不知道如何对排队的字符串进行处理,但是我猜测它会更好地解决问题。让我们说这个队列包含要检索的URL。)

: 这是不可能的,因为线程池执行程序需要一个runnables队列(没有上限通配符 - 只是普通的runnables)。但是队列可以更改为工作队列(BlockingQueue<Runnable>),这样在向队列添加元素时,BooleanSupplier将指示元素是否可用。这样,排队的元素不需要是某种类型,而是可以运行。添加元素可能如下所示(E是Runnable):

public boolean add(E element, BooleanSupplier availability) {
    lock.lock();
    try {
        if (data.putIfAbsent(element, availability) == null) {
            notEmpty.signal();
            return true;
        }
        return false;
    } finally {
        lock.unlock();
    }
}

String url = "...";
queue.add(() -> wget(url), () -> unlockedStrings.contains(url));

对于BlockingQueue合同:

@Override
public boolean add(E element) {
    return add(element, () -> Boolean.TRUE);
}

必须使用一些worker启动线程池:

ThreadPoolExecutor workerPool = new ThreadPoolExecutor(minWorkers, maxWorkers, 30L, TimeUnit.SECONDS, queue);
workerPool.prestartAllCoreThreads();