Java 使用ScheduledThreadPoolExecutor创建有界队列的最佳方法是什么?
Sun Java(1.6)Java 使用ScheduledThreadPoolExecutor创建有界队列的最佳方法是什么?,java,concurrency,scheduled-tasks,threadpool,Java,Concurrency,Scheduled Tasks,Threadpool,Sun Java(1.6)ScheduledThreadPoolExecutor是ThreadPoolExecutor的扩展,它在内部使用了DelayQueue的一个实现,这是一个无限队列。我需要的是一个具有有界队列的ScheduledThreadpoolExecutor,即它对队列中累积的任务有一个限制,以便当队列中的任务超过限制时,它开始拒绝进一步提交的任务,并防止JVM内存不足 令人惊讶的是,google或stackoverflow并没有向我指出讨论这个问题的任何结果。有没有我错过的东西?
ScheduledThreadPoolExecutor
是ThreadPoolExecutor
的扩展,它在内部使用了DelayQueue
的一个实现,这是一个无限队列。我需要的是一个具有有界队列的ScheduledThreadpoolExecutor
,即它对队列中累积的任务有一个限制,以便当队列中的任务超过限制时,它开始拒绝进一步提交的任务,并防止JVM内存不足
令人惊讶的是,google或stackoverflow并没有向我指出讨论这个问题的任何结果。有没有我错过的东西?如果没有,我如何实现ScheduledThreadpoolExecutor以最佳方式为我提供预期的功能 如果您确实不想重新实现ScheduledThreadPoolExecutor
,那么您可以扩展它,覆盖所有schedule*
方法,并实现自己的任务边界。不过,这会相当糟糕:
private final Object scheduleMonitor = new Object();
@Override
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit)
{
if (command == null || unit == null)
throw new NullPointerException();
synchronized (scheduleMonitor)
{
while (getQueue().size() >= MAX_QUEUE_SIZE)
{
scheduleMonitor.wait();
}
super.schedule(command, delay, unit);
}
}
@Override
Runnable getTask()
{
final Runnable r = getTask();
synchronized (scheduleMonitor)
{
scheduleMonitor.notify();
}
return r;
}
private final Object scheduleMonitor=new Object();
@凌驾
公共调度未来调度(Runnable命令,
久拖不决,,
时间单位(单位)
{
if(command==null | | unit==null)
抛出新的NullPointerException();
已同步(scheduleMonitor)
{
while(getQueue().size()>=最大队列大小)
{
scheduleMonitor.wait();
}
超级时间表(命令、延迟、单位);
}
}
@凌驾
可运行getTask()
{
最终可运行r=getTask();
已同步(scheduleMonitor)
{
scheduleMonitor.notify();
}
返回r;
}
重复以下步骤:
公共调度未来调度(可调用、长延迟、时间单位)
public ScheduledFuture scheduleAtFixedRate(Runnable命令, 长时间的延迟, 长期以来,, 时间单位)
public ScheduledFuture scheduleWithFixedDelay(Runnable命令, 长时间的延迟, 久拖不决,, 时间单位)
另一个警告是,我没有通过调用
super.schedule
检查任何死锁问题,同时在scheduleMonitor
上保持锁。…如何以不同方式处理它,即根据队列大小延迟任务子任务。executor服务通过getQueue()公开队列。您可以对其调用size(),根据您计划的队列大小限制,您可以开始拒绝任务或开始延迟任务执行(增加计划时间,将队列大小作为其中一个因素)
总而言之,这又不是最好的解决方案;仅供参考,java提供了延迟队列来支持工作窃取。正如其他人已经指出的,没有现成的方法可以做到这一点。只要确保你尝试使用“组合”而不是“继承”。创建一个新类,该类实现必要的接口,并通过对必要的方法执行所需的检查,将其委托给底层的
ScheduledThreadPoolExecutor
还可以通过简单的修改使用中指定的技术。您可以使用
Semaphore#acquire
,而不是使用Semaphore#tryAcquire
,并根据布尔结果决定是否需要调用拒绝处理程序。仔细想想,我个人觉得,库作者直接对特定的执行器进行子类化,而不是依赖组合来创建一个“可调度”包装器,这是一种疏忽。最简单的解决方法是使用调度的执行器只调度任务,而不是实际执行它们。调度程序必须显式检查执行器队列大小,如果执行器队列高于阈值,则放弃任务
另一个选项是在ScheduledTask中检查ScheduledThreadPoolExecutor队列大小是否正确。如果队列高于阈值,请立即返回。在这种情况下,任务将立即执行并从队列中删除。所以不会发生溢出。ScheduledThreadPoolExecutor不使用队列作为字段,而是调用getQueue。但它调用super.getQueue,这是来自ThreadPoolExecutor的队列。您可以使用反射来覆盖它,如下所示:
public class BoundedScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {
public BoundedScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler, int queueCapacity) {
super(corePoolSize, handler);
setMaximumPoolSize(corePoolSize);
setKeepAliveTime(0, TimeUnit.MILLISECONDS);
LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(queueCapacity) {
@Override
public boolean add(Runnable r) {
boolean added = offer(r);
if (added) {
return added;
} else {
getRejectedExecutionHandler().rejectedExecution(r, CrashingThreadPoolExecutor.this);
return false;
}
}
};
try {
Field workQueueField = ThreadPoolExecutor.class.getDeclaredField("workQueue");
workQueueField.setAccessible(true);
workQueueField.set(this, queue);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
公共类BoundedScheduledThreadPoolExecutor扩展ScheduledThreadPoolExecutor{
public BoundedScheduledThreadPoolExecutor(int corePoolSize,RejectedExecutionHandler,int queueCapacity){
super(corePoolSize,handler);
setMaximumPoolSize(corePoolSize);
setKeepAliveTime(0,时间单位为毫秒);
LinkedBlockingQueue=新建LinkedBlockingQueue(队列容量){
@凌驾
公共布尔加法(可运行的r){
布尔加法=报价(r);
如有(新增){
增加了退货;
}否则{
getRejectedExecutionHandler().rejectedExecution(r,CrashingThreadPoolExecutor.this);
返回false;
}
}
};
试一试{
Field workQueueField=ThreadPoolExecutor.class.getDeclaredField(“工作队列”);
workQueueField.setAccessible(true);
workQueueField.set(此,队列);
}捕获(例外e){
抛出新的运行时异常(e);
}
}
}
唉,您可能需要自己重新实现ScheduledThreadPoolExecutor
,因为我看不到一种方法可以让您自定义