Java併發編程之ScheduledThreadPoolExecutor源碼剖析

  之前我們剖析過Java中線程池ThreadPoolExecutor的源碼,鏈接爲 https://blog.csdn.net/HappyHeng/article/details/86827324,這一節從源碼層次講一下ScheduledThreadPoolExecutor這個定時線程池是如何執行任務的。

一、ScheduledFutureTask :

  在ThreadPoolExecutor中提交任務執行,只需要提交一個execute(Runnable)即可,ThreadPoolExecutor內部的queue也是存儲Runnable即可。但是在定時線程池中,又將可執行的代碼封裝成一個ScheduledFutureTask,此Task中封裝了Runnable的執行策略與執行時間,下面來看一下其成員變量:

private long time; // 此爲下次執行時機的納秒數

private final long period; // 重複任務的週期(以納秒爲單位)。正值表示固定速率的執行。負值表示固定延遲執行。值0表示非重複任務。

private Callable<V> callable; // 內部使用callable來保存傳入的Runnable

下面講一下線程拿到task後,其執行run()方法的解析:

        public void run() {
            boolean periodic = isPeriodic();
            // 當前的狀態是否允許執行
            if (!canRunInCurrentRunState(periodic))
                cancel(false);
            // periodic表示固定時間段執行,若爲!periodic,則表示不按固定時間段執行,只執行一次即可
            else if (!periodic)
                ScheduledFutureTask.super.run();
            // 若爲periodic,則調用父類的runAndReset()執行相關代碼
            else if (ScheduledFutureTask.super.runAndReset()) {
                // 執行完成後,設置下一次執行的時間,將時間設置到time字段中
                setNextRunTime();
                // 將task重新插入到queue隊列中
                reExecutePeriodic(outerTask);
            }
        }

 

二、DelayedWorkQueue:

  看完了ScheduledFutureTask之後,我們知道Task分爲只執行一次與固定速率執行的兩種task,那麼定時線程池是如何等待指定時間纔去執行task呢?

  我們知道,在ThreadPoolExecutor中,線程池會阻塞在blockQueue中,直到有runnable存入。而ScheduledFutureTask中,是新寫了支持延時執行的Queue來達到定時執行的目的,即爲DelayedWorkQueue,我們看一下其take()方法:

        public RunnableScheduledFuture<?> take() throws InterruptedException {
            final ReentrantLock lock = this.lock;
	    	// 在執行之前首先要獲取到鎖
            lock.lockInterruptibly();
            try {
                for (;;) {
					// 在queue中,會根據執行時間將task排序,此爲獲取到第一個執行的task
                    RunnableScheduledFuture<?> first = queue[0];
                    if (first == null)
                    	// 如果queue中沒有task,則一直等待
                        available.await();
                    else {
                    	// 得到第一個執行的task與現在時間的間隔
                        long delay = first.getDelay(NANOSECONDS);
                        if (delay <= 0)
                        	// 若小與等於0,說明需要買上執行,則返回此task
                            return finishPoll(first);
                        first = null; // don't retain ref while waiting
                        // leader爲正在等待的線程
                        if (leader != null)
                        	// 如果此時有線程在等待,那麼當前線程需要一直等待
                            available.await();
                        else {
                            Thread thisThread = Thread.currentThread();
                            leader = thisThread;
                            try {
                            	// 如果未有線程等待,則當前線程等待第一個task的執行時間
                                available.awaitNanos(delay);
                            } finally {
                                if (leader == thisThread)
                                    leader = null;
                            }
                        }
                    }
                }
            } finally {
            	// 當執行完成時,調用signal()來喚起其它線程
                if (leader == null && queue[0] != null)
                    available.signal();
                lock.unlock();
            }
        }

  上述簡單的分析了ScheduledThreadPoolExecutor的等待與獲取task的過程,以及task的執行過程,如果以後有時間的話,會以圖畫的形式來分析一下ScheduledThreadPoolExecutor的執行過程。

 

 

 

 

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