Java 使用ScheduledThreadPoolExecutor创建有界队列的最佳方法是什么?

Java 使用ScheduledThreadPoolExecutor创建有界队列的最佳方法是什么?,java,concurrency,scheduled-tasks,threadpool,Java,Concurrency,Scheduled Tasks,Threadpool,Sun Java(1.6)ScheduledThreadPoolExecutor是ThreadPoolExecutor的扩展,它在内部使用了DelayQueue的一个实现,这是一个无限队列。我需要的是一个具有有界队列的ScheduledThreadpoolExecutor,即它对队列中累积的任务有一个限制,以便当队列中的任务超过限制时,它开始拒绝进一步提交的任务,并防止JVM内存不足 令人惊讶的是,google或stackoverflow并没有向我指出讨论这个问题的任何结果。有没有我错过的东西?

Sun Java(1.6)
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
,因为我看不到一种方法可以让您自定义