简体   繁体   English

如何限制任务,以便一次只运行一个任务,然后忽略休息

[英]How to throttle tasks such that only one runs at a time and rest are ignored

I have an object that listens to external events. 我有一个侦听外部事件的对象。 Upon receiving an event, my object needs to execute a task (a Runnable ). 收到事件后,我的对象需要执行任务( Runnable )。 However, there is a restriction: 但是,有一个限制:

Once the task starts executing, I should not start other tasks (I can ignore them) until original task is finished and certain amount of time passed after that (throttling). 一旦任务开始执行,我就不应该开始其他任务(我可以忽略它们),直到原始任务完成并且在那之后经过一定量的时间(限制)。

Here's suggested implementation using semaphore : 这里建议使用信号量实现:

public class Sample {

    private final Semaphore semaphore = new Semaphore(1);

    private final ScheduledExecutorService executor;

    public Sample(ScheduledExecutorService executor) {
        this.executor = executor;    
    }

    public void tryRun() {
        if (semaphore.tryAcquire()) {
            try {
                executor.submit(
                    new Runnable() {
                        @Override
                        public void run() {
                            try {
                                doIt();
                            } finally {
                                try {
                                    executor.schedule(
                                        new Runnable() {
                                            @Override
                                            public void run() {
                                                semaphore.release();
                                            }
                                        },
                                        1, 
                                        TimeUnit.MINUTES
                                    );
                                } catch (Throwable t) {
                                    semaphore.release();
                                }
                            }
                        }
                    }
                );
            } catch (Throwable t) {
                semaphore.release();            
            }
        }
    }

    private void doIt() {
        // the exact task executing logic is here
    }
}

The code seems too verbose to me. 代码对我来说似乎太冗长了。 Is there a better way of doing this? 有没有更好的方法呢?

PS Another restriction is that ScheduledExecutorService is the only interface I have to the external executor and I cannot start my own threads/executors within my object PS另一个限制是ScheduledExecutorService是我对外部执行器的唯一接口,我不能在我的对象中启动自己的线程/执行器

Just use a ThreadPoolExecutor with a single thread. 只需使用一个ThreadPoolExecutor和一个线程。 You'll have to decide whether it's okay to queue up a lot of the other tasks. 你必须决定是否可以排队很多其他任务。 If not, pass it your own BlockingQueue with capacity 1, I believe this will let at most one task queue up. 如果没有,将它传递给你自己的容量为1的BlockingQueue ,我相信这最多可以让一个任务排队。 I'm thinking about how to get that down to zero. 我正在考虑如何将其降至零。

One way to create a single threaded executor with unbounded queue. 创建具有无界队列的单线程执行程序的一种方法。

Note you will need to configure the TPE to discard rejected tasks via a DiscardPolicy . 请注意,您需要配置TPE以通过DiscardPolicy丢弃被拒绝的任务。

+1 to @djechlin. +1到@djechlin。 That's the right answer. 这是正确的答案。 To add the implementation: 要添加实现:

ExecutorService threadPool =
   new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
       new SynchronousQueue<Runnable>(), new ThreadPoolExecutor.DiscardPolicy());

To go back to your question. 回到你的问题。

Once the task starts executing, I should not start other tasks (I can ignore them) until original task is finished and certain amount of time passed after that (throttling). 一旦任务开始执行,我就不应该开始其他任务(我可以忽略它们),直到原始任务完成并且在那之后经过一定量的时间(限制)。

This starts 1 and only 1 thread, uses a synchronous-queue and the DiscardPolicy which will discard any tasks unless the thread is waiting to run it. 这将启动1并且只有1个线程,使用同步队列和DiscardPolicy ,它将丢弃任何任务,除非线程正在等待运行它。 If the single thread is working then any tasks submitted to the pool will be rejected and sent to the rejection policy. 如果单个线程正在运行,那么提交给池的任何任务都将被拒绝并发送到拒绝策略。 In your case you want to just discard them. 在你的情况下,你想要丢弃它们。

If you really want to queue the tasks if the working thread is busy, then you should use a bounded LinkedBlockingQueue instead. 如果你真的想在工作线程繁忙的情况下排队任务,那么你应该使用有界的LinkedBlockingQueue代替。 Maybe with a rejection policy that blocks the caller when the queue is full. 可能使用拒绝策略在队列满时阻止调用者。

Something like the following: 类似于以下内容:

ExecutorService threadPool =
   new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
      new LinkedBlockingQueue<Runnable>(10), new RejectedExecutionHandler() {
         public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
           // this will block the caller until the queue has space
           executor.getQueue().add(r);
         }
      });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM