簡體   English   中英

如何實現ExecutorService以輪換方式執行任務?

[英]How to implement an ExecutorService to execute tasks on a rotation-basis?

我正在使用帶有固定線程池的 java.util.concurrent.ExecutorService來執行任務列表。 我的任務列表通常大約為80-150,我將任何時候運行的線程數限制為10,如下所示:

ExecutorService threadPoolService = Executors.newFixedThreadPool(10);

for ( Runnable task : myTasks ) 
{     
    threadPoolService.submit(task); 
}

我的用例要求甚至已完成的任務應該再次重新提交給ExecutorService,但只有在所有提交的任務都得到服務/完成時才應該執行/再次執行。 基本上,提交的任務應該以輪換方式執行。 因此,在這種情況下,不會有threadPoolService.shutdown()threadPoolService.shutdownNow()調用。

我的問題是,如何實現ExecutorService服務輪換基礎任務?

ThreadPoolExecutor為afterExecution提供了一個擴展點,您可以將作業放回隊列的末尾。

public class TaskRepeatingThreadPoolExecutor extends ThreadPoolExecutor {

    public TaskRepeatingThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        this.submit(r);
    }
}

當然,如果沒有ExecutorService方便的工廠方法的幫助,你必須做更多的工作來自己實例化它,但構造函數很簡單,可以理解。

答案與用於ExecutorService實例的工作隊列的實現更相關。 所以,我建議:

  1. 首先選擇提供循環隊列功能的java.util.concurrent.BlockingQueue示例 )的實現。 注意 ,選擇BlockingQueue的原因是要等到下一個任務提供給隊列; 因此,在循環+阻塞隊列的情況下,您應該小心如何提供相同的行為和功能。

  2. 而不是使用Executors.new...來創建一個新的ThreadPoolExecutor使用直接構造函數 ,如

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)

這樣,除非您命令執行程序shutdown ,否則它將嘗試從隊列中獲取下一個任務,以便從其工作隊列中執行,該隊列是任務的循環容器。

我建議以下解決方案完全使用標准庫並發工具中存在的功能。 它使用帶有任務裝飾器類的CyclicBarrier和重新提交所有任務的屏障操作:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Rotation {

    private static final class RotationDecorator implements Runnable {
        private final Runnable          task;
        private final CyclicBarrier barrier;


        RotationDecorator( Runnable task, CyclicBarrier barrier ) {
            this.task = task;
            this.barrier = barrier;
        }


        @Override
        public void run() {
            this.task.run();
            try {
                this.barrier.await();
            } catch(InterruptedException e) {
                ; // Consider better exception handling
            } catch(BrokenBarrierException e) {
                ; // Consider better exception handling
            }
        }
    }


    public void startRotation( List<Runnable> tasks ) {
        final ExecutorService threadPoolService = Executors.newFixedThreadPool( 10 );
        final List<Runnable> rotatingTasks = new ArrayList<Runnable>( tasks.size() );
        final CyclicBarrier barrier = new CyclicBarrier( tasks.size(), new Runnable() {
            @Override
            public void run() {
                Rotation.this.enqueueTasks( threadPoolService, rotatingTasks );
            }
        } );
        for(Runnable task : tasks) {
            rotatingTasks.add( new RotationDecorator( task, barrier ) );
        }
        this.enqueueTasks( threadPoolService, rotatingTasks );
    }


    private void enqueueTasks( ExecutorService service, List<Runnable> tasks ) {
        for(Runnable task : tasks) {
            service.submit( task );
        }
    }

}

您可以簡單地檢查所有任務是否已執行,並在情況發生時重新提交,例如:

    List<Future> futures = new ArrayList<>();
    for (Runnable task : myTasks) {
        futures.add(threadPoolService.submit(task));
    }
    //wait until completion of all tasks
    for (Future f : futures) {
        f.get();
    }
    //restart
    ......

編輯
您似乎希望在任務完成后立即重新提交。 您可以使用ExecutorCompletionService ,它允許您在執行任務時檢索任務, - 請參閱下面的一個簡單示例,其中包含2個任務,一旦完成就會重新提交幾次。 樣本輸出:

任務1提交了pool-1-thread-1
任務2提交了pool-1-thread-2
任務1完成了pool-1-thread-1
任務1提交了pool-1-thread-3
任務2完成了pool-1-thread-2
任務1完成了pool-1-thread-3
任務2提交了pool-1-thread-4
任務1提交了pool-1-thread-5
任務1完成了pool-1-thread-5
任務2完成了pool-1-thread-4

public class Test1 {

    public final ConcurrentMap<String, String> concurrentMap = new ConcurrentHashMap<>();
    public final AtomicInteger retries = new AtomicInteger();
    public final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        int count = 0;
        List<Runnable> myTasks = new ArrayList<>();
        myTasks.add(getRunnable(1));
        myTasks.add(getRunnable(2));
        ExecutorService threadPoolService = Executors.newFixedThreadPool(10);
        CompletionService<Runnable> ecs = new ExecutorCompletionService<Runnable>(threadPoolService);
        for (Runnable task : myTasks) {
            ecs.submit(task, task);
        }
        //wait until completion of all tasks
        while(count++ < 3) {
            Runnable task = ecs.take().get();
            ecs.submit(task, task);
        }
        threadPoolService.shutdown();
    }

    private static Runnable getRunnable(final int i) {
        return new Runnable() {

            @Override
            public void run() {
                System.out.println("Task " + i + " submitted " + Thread.currentThread().getName() + "  ");
                try {
                    Thread.sleep(500 * i);
                } catch (InterruptedException ex) {
                    System.out.println("Interrupted");
                }
                System.out.println("Task " + i + " completed " + Thread.currentThread().getName() + "  ");
            }
        };
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM