2

外部イベントをリッスンするオブジェクトがあります。イベントを受け取ると、オブジェクトはタスク ( Runnable ) を実行する必要があります。ただし、次の制限があります。

タスクの実行が開始されたら、元のタスクが終了してから一定の時間が経過するまで (スロットリング)、他のタスクを開始しないでください (無視できます)。

セマフォを使用した推奨実装は次のとおりです。

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もう1つの制限は、ScheduledExecutorServiceが外部エグゼキューターへの唯一のインターフェースであり、オブジェクト内で独自のスレッド/エグゼキューターを開始できないことです

4

2 に答える 2

5

@djechlinに+1。それが正しい答えです。実装を追加するには:

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

あなたの質問に戻ります。

タスクの実行が開始されたら、元のタスクが終了してから一定の時間が経過するまで (スロットリング)、他のタスクを開始しないでください (無視できます)。

これは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 に答える