使用ExecutorService來停止線程服務


使用ExecutorService來停止線程服務

之前的文章中我們提到了ExecutorService可以使用shutdown和shutdownNow來關閉。

這兩種關閉的區別在於各自的安全性和響應性。shutdownNow強行關閉速度更快,但是風險也更大,因爲任務可能正在執行的過程中被結束了。而shutdown正常關閉雖然速度比較慢,但是卻更安全,因爲它一直等到隊列中的所有任務都執行完畢之後才關閉。

使用shutdown

我們先看一個使用shutdown的例子:

    public void useShutdown() throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(10);

        Runnable runnableTask = () -> {
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        executor.submit(runnableTask);
        executor.shutdown();
        executor.awaitTermination(800, TimeUnit.MILLISECONDS);
    }

awaitTermination將會阻塞直到所有正在執行的任務完成,或者達到指定的timeout時間。

使用shutdownNow

當通過shutdownNow來強行關閉ExecutorService是, 它會嘗試取消正在執行的任務,並返回所有已經提交但是還沒有開始的任務。從而可以將這些任務保存起來,以便以後進行處理。

但是這樣我們只知道了還沒有開始執行的任務,對於那些已經開始執行但是沒有執行完畢卻被取消的任務我們無法獲取。

我們看下如何獲得開始執行但是還沒有執行完畢的任務:

public class TrackingExecutor extends AbstractExecutorService {
    private final ExecutorService executorService;
    private final Set<Runnable> taskCancelledAtShutdown= Collections.synchronizedSet(new HashSet<Runnable>());

    public TrackingExecutor(ExecutorService executorService){
         this.executorService=executorService;
    }
    @Override
    public void shutdown() {
        executorService.shutdown();
    }

    @Override
    public List<Runnable> shutdownNow() {
        return executorService.shutdownNow();
    }

    @Override
    public boolean isShutdown() {
        return executorService.isShutdown();
    }

    @Override
    public boolean isTerminated() {
        return executorService.isTerminated();
    }

    @Override
    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
        return executorService.awaitTermination(timeout,unit);
    }

    @Override
    public void execute(Runnable command) {
        executorService.execute(() -> {
            try {
                command.run();
            }finally {
                if(isShutdown() && Thread.currentThread().isInterrupted()){
                    taskCancelledAtShutdown.add(command);
                }
            }
        });
    }

    public List<Runnable> getCancelledTask(){
        if(! executorService.isTerminated()){
            throw new IllegalStateException("executorService is not terminated");
        }
        return new ArrayList<>(taskCancelledAtShutdown);
    }
}

上面的例子中我們構建了一個新的ExecutorService,他傳入一個ExecutorService,並對其進行封裝。

我們重寫了execute方法,在執行完畢判斷該任務是否被中斷,如果被中斷則將其添加到CancelledTask列表中。

並提供一個getCancelledTask方法來返回未執行完畢的任務。

我們看下怎麼使用:

    public void useShutdownNow() throws InterruptedException {
        TrackingExecutor trackingExecutor=new TrackingExecutor(Executors.newCachedThreadPool());

        Runnable runnableTask = () -> {
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        trackingExecutor.submit(runnableTask);
        List<Runnable> notrunList=trackingExecutor.shutdownNow();
        if(trackingExecutor.awaitTermination(800, TimeUnit.SECONDS)){
            List<Runnable> runButCancelledList= trackingExecutor.getCancelledTask();
        }
    }

trackingExecutor.shutdownNow()返回的是未執行的任務。而trackingExecutor.getCancelledTask()返回的是被取消的任務。

上面的任務其實還有一個缺點,因爲我們在存儲被取消的任務列表的額時候taskCancelledAtShutdown.add(command),因爲之前的判斷不是原子操作,則可能會產生誤報。

本文的例子請參考https://github.com/ddean2009/learn-java-concurrency/tree/master/ExecutorServiceShutdown

更多內容請參考http://www.flydean.com/java-shutdown-executorservice/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章