2

我有一个监听外部事件的对象。收到事件后,我的对象需要执行一个任务(一个Runnable)。但是,有一个限制:

一旦任务开始执行,我不应该开始其他任务(我可以忽略它们),直到原始任务完成并且在那之后经过一定的时间(节流)。

这是使用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
    }
}

代码对我来说似乎太冗长了。有没有更好的方法来做到这一点?

PS 另一个限制是ScheduledExecutorService是我对外部执行程序的唯一接口,我无法在我的对象中启动我自己的线程/执行程序

4

2 回答 2

5

Just use a ThreadPoolExecutor with a single thread. 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. 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.

于 2013-05-21T15:05:15.147 回答
5

+1 给@djechlin。这是正确的答案。添加实现:

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

回到你的问题。

一旦任务开始执行,我不应该开始其他任务(我可以忽略它们),直到原始任务完成并且在那之后经过一定的时间(节流)。

这将启动 1 个且仅 1 个线程,使用同步队列,DiscardPolicy除非线程正在等待运行它,否则它将丢弃任何任务。如果单线程正在工作,则提交到池的任何任务都将被拒绝并发送到拒绝策略。在您的情况下,您只想丢弃它们。

如果你真的想在工作线程很忙的情况下将任务排队,那么你应该使用有界的LinkedBlockingQueue。也许有一个拒绝策略,当队列已满时阻止调用者。

类似于以下内容:

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);
         }
      });
于 2013-05-21T15:31:49.650 回答