我已经实现了一个存储类似于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();
答案 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();