Java 向ThreadPoolExecutor的BlockingQueue添加任务是否可取?
对于将任务直接添加到支持执行器的Java 向ThreadPoolExecutor的BlockingQueue添加任务是否可取?,java,concurrency,producer-consumer,executorservice,blockingqueue,Java,Concurrency,Producer Consumer,Executorservice,Blockingqueue,对于将任务直接添加到支持执行器的BlockingQueue中是否可以接受,JavaDoc for尚不清楚。调用executor.getQueue()主要用于调试和监视 我正在用自己的阻塞队列构建线程池执行器。我保留对队列的引用,以便可以直接向其中添加任务。相同的队列由getQueue()返回,因此我假设getQueue()中的警告适用于通过我的方法获取的对后备队列的引用 例子 代码的一般模式是: int n = ...; // number of threads queue = new Arra
BlockingQueue
中是否可以接受,JavaDoc for尚不清楚。调用executor.getQueue()主要用于调试和监视
我正在用自己的
阻塞队列构建线程池执行器
。我保留对队列的引用,以便可以直接向其中添加任务。相同的队列由getQueue()
返回,因此我假设getQueue()
中的警告适用于通过我的方法获取的对后备队列的引用
例子
代码的一般模式是:
int n = ...; // number of threads
queue = new ArrayBlockingQueue<Runnable>(queueSize);
executor = new ThreadPoolExecutor(n, n, 1, TimeUnit.HOURS, queue);
executor.prestartAllCoreThreads();
// ...
while (...) {
Runnable job = ...;
queue.offer(job, 1, TimeUnit.HOURS);
}
while (jobsOutstanding.get() != 0) {
try {
Thread.sleep(...);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
executor.shutdownNow();
int n=…;//线程数
队列=新的ArrayBlockingQueue(queueSize);
executor=新线程池executor(n,n,1,TimeUnit.HOURS,queue);
executor.prestartalcorethreads();
// ...
而(…){
可运行作业=。。。;
队列.报价(工作,1,时间单位.小时);
}
while(jobsOutstanding.get()!=0){
试一试{
睡眠(…);
}捕捉(中断异常e){
Thread.currentThread().interrupt();
}
}
执行者。关机现在();
queue.offer()
vsexecutor.execute()
据我所知,典型的用法是通过executor.execute()
添加任务。上面示例中的方法有阻塞队列的好处,而execute()
如果队列已满并拒绝我的任务,则会立即失败。我还喜欢提交作业与阻塞队列交互;这让我觉得更“纯粹”的生产者和消费者
直接向队列中添加任务的含义是:我必须调用prestartalcorethreads()
,否则没有工作线程在运行。假设没有其他与执行器的交互,则不会监视队列(对ThreadPoolExecutor
source的检查证实了这一点)。这也意味着对于直接排队而言,ThreadPoolExecutor
必须另外为>0个核心线程配置,并且不得配置为允许核心线程超时
tl;博士
给定一个线程池执行器
,配置如下:
- 核心线程>0
- 核心线程不允许超时
- 芯线是预先开始的
- 保留对支持执行器的
阻塞队列
的引用
是否可以直接将任务添加到队列中,而不是调用executor.execute()
相关的
这个问题()与此类似,但并不特别涉及直接添加到队列。如果是我,我更喜欢使用Executor\execute()
而不是queue\offer()
,因为我已经在使用java.util.concurrent
中的所有其他内容了
您的问题很好,引起了我的兴趣,因此我查看了ThreadPoolExecutor\execute()
的源代码:
我们可以看到,execute本身在工作队列上调用offer()
,但如果需要的话,在执行一些漂亮、美味的池操作之前是不会这样做的。出于这个原因,我认为最好使用execute()
;不使用它可能(虽然我不确定)导致池以非最佳方式运行。但是,我不认为使用offer()
会破坏执行器-看起来任务是通过以下方式从队列中拉出的(同样来自ThreadPoolExecutor):
这个getTask()
方法只是从循环中调用的,因此如果执行器没有关闭,它将阻塞,直到新任务被赋予队列(不管它来自何处)
注意:尽管我在这里发布了源代码的代码片段,但我们不能依赖它们来获得最终的答案——我们应该只针对API进行编码。我们不知道随着时间的推移,execute()
的实现将如何变化。一个技巧是实现ArrayBlockingQueue的自定义子类,并重写offer()方法来调用阻塞版本,然后您仍然可以使用正常的代码路径
queue = new ArrayBlockingQueue<Runnable>(queueSize) {
@Override public boolean offer(Runnable runnable) {
try {
return offer(runnable, 1, TimeUnit.HOURS);
} catch(InterruptedException e) {
// return interrupt status to caller
Thread.currentThread().interrupt();
}
return false;
}
};
queue=new ArrayBlockingQueue(queueSize){
@覆盖公共布尔值提供(可运行可运行){
试一试{
返回报价(可运行,1,时间单位:小时);
}捕捉(中断异常e){
//向调用者返回中断状态
Thread.currentThread().interrupt();
}
返回false;
}
};
(正如您可能猜到的,我认为直接在队列上调用offer作为正常的代码路径可能是个坏主意)。通过在实例化时指定RejectedExecutionHandler,可以在队列已满时配置池的行为ThreadPoolExecutor
将四个策略定义为内部类,包括AbortPolicy
、DiscardOldestPolicy
、DiscardorPolicy
,以及我个人最喜欢的CallerRunPolicy
,它在控制线程中运行新作业
例如:
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
nproc, // core size
nproc, // max size
60, // idle timeout
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(4096, true), // Fairness = true guarantees FIFO
new ThreadPoolExecutor.CallerRunsPolicy() ); // If we have to reject a task, run it in the calling thread.
在某个时刻,必须访问队列。这样做的最佳位置是在一个自包含的RejectedExecutionHandler
中,它可以保存由于在池对象范围内直接操作队列而产生的任何代码重复或潜在错误。请注意,ThreadPoolExecutor
中包含的处理程序本身使用getQueue()
如果您使用的队列与内存中的标准LinkedBlockingQueue
或ArrayBlockingQueue
完全不同,这是一个非常重要的问题
例如,如果您在不同的机器上使用多个生产者实现生产者-消费者模式,并使用基于单独持久化子系统(如Redis)的排队机制,那么问题本身就变得相关了,即使您不希望像OP一样使用阻塞提供()
因此,给定的答案是必须调用prestartalcorethreads()
(或者调用足够的次数prestar
queue = new ArrayBlockingQueue<Runnable>(queueSize) {
@Override public boolean offer(Runnable runnable) {
try {
return offer(runnable, 1, TimeUnit.HOURS);
} catch(InterruptedException e) {
// return interrupt status to caller
Thread.currentThread().interrupt();
}
return false;
}
};
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
nproc, // core size
nproc, // max size
60, // idle timeout
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(4096, true), // Fairness = true guarantees FIFO
new ThreadPoolExecutor.CallerRunsPolicy() ); // If we have to reject a task, run it in the calling thread.
public class BlockingPolicy implements RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
executor.getQueue.put(r); // Self contained, no queue reference needed.
}
final CountDownLatch taskCounter = new CountDownLatch(TASKCOUNT);
final List<Runnable> taskParking = new LinkedList<Runnable>();
BlockingQueue<Runnable> taskPool = new ArrayBlockingQueue<Runnable>(1);
RejectedExecutionHandler rejectionHandler = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.err.println(Thread.currentThread().getName() + " -->rejection reported - adding to parking lot " + r);
taskCounter.countDown();
taskParking.add(r);
}
};
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 1000, TimeUnit.SECONDS, taskPool, rejectionHandler);
for(int i=0 ; i<TASKCOUNT; i++){
//main
threadPoolExecutor.submit(getRandomTask());
}
taskCounter.await(TASKCOUNT * 5 , TimeUnit.SECONDS);
System.out.println("Checking the parking lot..." + taskParking);
while(taskParking.size() > 0){
Runnable r = taskParking.remove(0);
System.out.println("Running from parking lot..." + r);
if(taskParking.size() > LIMIT){
waitForSometime(...);
}
threadPoolExecutor.submit(r);
}
threadPoolExecutor.shutdown();