Java實現終止線程池中正在運行的定時任務

源於開發

最近項目中遇到了一個新的需求,就是實現一個能夠動態添加定時任務的功能。說到這裏,有人可能會說簡單啊,使用quartz就行了,簡單粗暴。然而quartz框架過重了,小項目根本很差操做啊。固然,也有人會說,jdk提供了timer的接口啊,徹底夠用啊。可是咱們項目的需求徹底是多線程的模型啊,而timer是單線程的,so,樓主最後仍是選擇了jdk的線程池。java

線程池是什麼

Java經過Executors提供四種線程池,分別爲:
newCachedThreadPool :建立一個可緩存線程池,若是線程池長度超過處理須要,可靈活回收空閒線程,若無可回收,則新建線程。
newFixedThreadPool : 建立一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。
newScheduledThreadPool : 建立一個定長線程池,支持定時及週期性任務執行。
newSingleThreadExecutor : 建立一個單線程化的線程池,它只會用惟一的工做線程來執行任務,保證全部任務按照指定順序(FIFO, LIFO, 優先級)執行。緩存

樓主項目中用到的是newScheduledThreadPool, 就這些吧,再多的樓主就班門弄斧了,Google一下,一大堆。多線程

線程池service的獲取

樓主經過單例模式來獲取線程池的service,代碼以下:併發

/** * 線程池建立. * @author wuhf * @date 2018/01/16 */
public class ThreadPoolUtils {

    private static ScheduledExecutorService executorService;

    private ThreadPoolUtils() {
        //手動建立線程池.
        executorService = new ScheduledThreadPoolExecutor(10,
                new BasicThreadFactory.Builder().namingPattern("syncdata-schedule-pool-%d").daemon(true).build());
    }

    private static class PluginConfigHolder {
        private final static ThreadPoolUtils INSTANCE = new ThreadPoolUtils();
    }

    public static ThreadPoolUtils getInstance() {
        return PluginConfigHolder.INSTANCE;
    }

    public ScheduledExecutorService getThreadPool(){
        return executorService;
    }

}

中斷某一個正在運行的線程代碼實現

廢話就很少說了,代碼以下:框架

/** * 中斷線程池的某個任務. */
public class InterruptThread implements Runnable {

    private int num;

    public InterruptThread (int num){
        this.num = num;
    }

    public static void main(String[] args) throws InterruptedException {

        Thread interruptThread = new Thread(new InterruptThread(1));
        ScheduledFuture<?> t = ThreadPoolUtils.getInstance().getThreadPool().scheduleAtFixedRate(interruptThread,0,2,
                TimeUnit.SECONDS);

        InterruptThread interruptThread1 = new InterruptThread(2);
        ThreadPoolUtils.getInstance().getThreadPool().scheduleAtFixedRate(interruptThread1,0,2,
                TimeUnit.SECONDS);

        InterruptThread interruptThread2 = new InterruptThread(3);
        ThreadPoolUtils.getInstance().getThreadPool().scheduleAtFixedRate(interruptThread2,0,2,
                TimeUnit.SECONDS);
        Thread.sleep(5000);

        //終止正在運行的線程interruptThread
        t.cancel(true);
        while (true){

        }
    }

    @Override
    public void run() {
        System.out.println("this is a thread" + num);
    }
}

踩坑記錄

樓主在使用以下代碼時,忽然想到當這個定時任務須要被中止時該如何中止線程運行ide

ThreadPoolUtils.getInstance().getThreadPool().scheduleAtFixedRate(interruptThread,0,2,
                TimeUnit.SECONDS);

既然我有這樣的需求,那就Google一下吧,找了大半圈,愣是沒找到相關資料,都是一些關於Java線程池的深刻分析。或者是全局變量啥的,並無找到令樓主滿意的解決方案。ui

既然沒有線程的那就扒一下scheduleAtFixedRate的底層源碼看看是什麼東西吧,果不其然我在源碼中看到了scheduleAtFixedRate方法的具體實現,發現他的返回值是ScheduledFuture。this

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit) {
        if (command == null || unit == null)
            throw new NullPointerException();
        if (period <= 0)
            throw new IllegalArgumentException();
        ScheduledFutureTask<Void> sft =
            new ScheduledFutureTask<Void>(command,
                                          null,
                                          triggerTime(initialDelay, unit),
                                          unit.toNanos(period));
        RunnableScheduledFuture<Void> t = decorateTask(command, sft);
        sft.outerTask = t;
        delayedExecute(t);
        return t;
    }

接着往下咱們再看看ScheduledFuture裏面有什麼東西吧,沒有讓樓主失望,看到了這個spa

public boolean cancel(boolean mayInterruptIfRunning) {
            boolean cancelled = super.cancel(mayInterruptIfRunning);
            if (cancelled && removeOnCancel && heapIndex >= 0)
                remove(this);
            return cancelled;
}

//從線程的運行隊列中移除當前線程
public boolean remove(Runnable task) {
        boolean removed = workQueue.remove(task);
        tryTerminate(); // In case SHUTDOWN and now empty
        return removed;
}

再往上查super.cancel(mayInterruptIfRunning)是什麼東西,咱們看到了這個,線程

//經過調用線程的interrupt方法終止線程運行
public boolean cancel(boolean mayInterruptIfRunning) {
        if (!(state == NEW &&
              UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {    // in case call to interrupt throws exception
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // final state
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
            finishCompletion();
        }
        return true;
    }

到這裏全部的問題都迎刃而解。

總結一下吧

項目中老是會遇到比較難搞的解決方案,當Google不太好找時,翻一下jdk的源碼或許也是一個不錯的方法。

參考文章

相關文章
相關標籤/搜索