1. 程式人生 > >java&android執行緒池-Executor框架之ThreadPoolExcutor&ScheduledThreadPoolExecutor淺析(多執行緒程式設計之三)

java&android執行緒池-Executor框架之ThreadPoolExcutor&ScheduledThreadPoolExecutor淺析(多執行緒程式設計之三)

java多執行緒-概念&建立啟動&中斷&守護執行緒&優先順序&執行緒狀態(多執行緒程式設計之一)
java多執行緒同步以及執行緒間通訊詳解&消費者生產者模式&死鎖&Thread.join()(多執行緒程式設計之二)
java&android執行緒池-Executor框架之ThreadPoolExcutor&ScheduledThreadPoolExecutor淺析(多執行緒程式設計之三)

無論是在java還是在android中其實使用到的執行緒池都基本是一樣的,因此本篇我們將來認識一下執行緒池Executor框架(相關知識點結合了併發程式設計藝術書以及Android開發藝術探索而總結),下面是本篇的主要知識點:

1.Executor框架淺析 首先我們得明白一個 問題,為什麼需要執行緒池?在java中,使用執行緒來執行非同步任務時,執行緒的建立和銷燬需要一定的開銷,如果我們為每一個任務建立一個新的執行緒來執行的話,那麼這些執行緒的建立與銷燬將消耗大量的計算資源。同時為每一個任務建立一個新執行緒來執行,這樣的方式可能會使處於高負荷狀態的應用最終崩潰。所以執行緒池的出現為解決這個問題帶來曙光。我們將線上程池中建立若干條執行緒,當有任務需要執行時就從該執行緒池中獲取一條執行緒來執行任務,如果一時間任務過多,超出執行緒池的執行緒數量,那麼後面的執行緒任務就進入一個等待佇列進行等待,直到執行緒池有執行緒處於空閒時才從等待佇列獲取要執行的任務進行處理,以此迴圈.....這樣就大大減少了執行緒建立和銷燬的開銷,也會緩解我們的應用處於超負荷時的情況。1.1Executor框架的兩級排程模型
在java執行緒啟動時會建立一個本地作業系統執行緒,當該java執行緒終止時,這個作業系統執行緒也會被回收。而每一個java執行緒都會被一對一對映為本地作業系統的執行緒,作業系統會排程所有的執行緒並將它們分別給可用的CPU。而所謂的對映方式是這樣實現的,在上層,java多執行緒程式通過把應用分為若干個任務,然後使用使用者級的排程器(Executor框架)將這些任務對映為固定數量的執行緒;在底層,作業系統核心將這些執行緒對映到硬體處理器上。這樣種兩級排程模型如下圖所示:從圖中我們可以看出,應用程式通過Executor框架控制上層的排程,而下層的排程由作業系統核心控制,下層的排程不受應用程式的控制。1.2 Executor框架的結構
Executor框架的結構主要包括3個部分1.任務:包括被執行任務需要實現的介面:Runnable介面或Callable介面2.任務的執行:包括任務執行機制的核心介面Executor,以及繼承自Executor的EexcutorService介面。Exrcutor有兩個關鍵類實現了ExecutorService介面(ThreadPoolExecutor和ScheduledThreadPoolExecutor)。3.非同步計算的結果:包括介面Future和實現Future介面的FutureTask類(這個我們放在下一篇文章說明)下面我們通過一個UML圖來認識一下這些類間的關係:Extecutor是一個介面,它是Executor框架的基礎,它將任務的提交與任務的執行分離開來。ThreadPoolExecutor是執行緒池的核心實現類,用來執行被提交的任務。ScheduledThreadPoolExecutor是一個實現類,可以在給定的延遲後執行命令,或者定期執行命令。ScheduledThreadPoolExecutor比Timer更靈活,功能更強大。Future介面和實現Future介面的FutureTask類,代表非同步計算的結果。Runnable介面和Callable介面的實現類,都可以被ThreadPoolExecutor或者ScheduledThreadPoolExecutor執行。區別就是Runnable無法返回執行結果,而Callable可以返回執行結果。下面我們通過一張圖來理解它們間的執行關係:分析說明:主執行緒首先建立實現Runnable或Callable介面的任務物件,工具類Executors可以把一個Runnable物件封裝為一個Callable物件,使用如下兩種方式:Executors.callable(Runnable task)或者Executors.callable(Runnable task,Object resule)。然後可以把Runnable物件直接提交給ExecutorService執行,方法為ExecutorService.execute(Runnable command);或者也可以把Runnable物件或者Callable物件提交給ExecutorService執行,方法為ExecutorService.submit(Runnable task)或ExecutorService.submit(Callable<T> task)。這裡需要注意的是如果執行ExecutorService.submit(...),ExecutorService將返回一個實現Future介面的物件(其實就是FutureTask)。當然由於FutureTask實現了Runnable介面,我們也可以直接建立FutureTask,然後提交給ExecutorService執行。到此Executor框架的主要體系結構我們都介紹完了,我們對此有了大概瞭解後,下面我們就重點聊聊兩個主要的執行緒池實現類。2.ThreadPoolExecutor淺析 ThreadPoolExecutor是執行緒的真正實現,通常使用工廠類Executors來建立,但它的構造方法提供了一系列引數來配置執行緒池,下面我們就先介紹ThreadPoolExecutor的構造方法中各個引數的含義。
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }
corePoolSize:執行緒池的核心執行緒數,預設情況下,核心執行緒數會一直線上程池中存活,即使它們處理閒置狀態。如果將ThreadPoolExecutor的allowCoreThreadTimeOut屬性設定為true,那麼閒置的核心執行緒在等待新任務到來時會執行超時策略,這個時間間隔由keepAliveTime所指定,當等待時間超過keepAliveTime所指定的時長後,核心執行緒就會被終止。maximumPoolSize:執行緒池所能容納的最大執行緒數量,當活動執行緒數到達這個數值後,後續的新任務將會被阻塞。keepAliveTime:非核心執行緒閒置時的超時時長,超過這個時長,非核心執行緒就會被回收。當ThreadPoolExecutor的allowCoreThreadTimeOut屬性設定為true時,keepAliveTime同樣會作用於核心執行緒。unit:用於指定keepAliveTime引數的時間單位,這是一個列舉,常用的有TimeUnit.MILLISECONDS(毫秒),TimeUnit.SECONDS(秒)以及TimeUnit.MINUTES(分鐘)等。workQueue:執行緒池中的任務佇列,通過執行緒池的execute方法提交Runnable物件會儲存在這個佇列中。threadFactory:執行緒工廠,為執行緒池提供建立新執行緒的功能。ThreadFactory是一個介面,它只有一個方法:Thread newThread(Runnable r)。除了上面的引數外還有個不常用的引數,RejectExecutionHandler,這個引數表示當ThreadPoolExecutor已經關閉或者ThreadPoolExecutor已經飽和時(達到了最大執行緒池大小而且工作佇列已經滿),execute方法將會呼叫Handler的rejectExecution方法來通知呼叫者,預設情況 下是丟擲一個RejectExecutionException異常。瞭解完相關建構函式的引數,我們再來看看ThreadPoolExecutor執行任務時的大致規則:(1)如果執行緒池的數量還未達到核心執行緒的數量,那麼會直接啟動一個核心執行緒來執行任務(2)如果執行緒池中的執行緒數量已經達到或者超出核心執行緒的數量,那麼任務會被插入到任務佇列中排隊等待執行。(3)如果在步驟(2)中無法將任務插入到任務佇列中,這往往是由於任務佇列已滿,這個時候如果執行緒數量未達到執行緒池規定的最大值,那麼會立刻啟動一個非核心執行緒來執行任務。(4)如果在步驟(3)中執行緒數量已經達到執行緒池規定的最大值,那麼就會拒絕執行此任務,ThreadPoolExecutor會呼叫RejectExecutionHandler的rejectExecution方法來通知呼叫者。到此ThreadPoolExecutor的詳細配置瞭解完了,ThreadPoolExecutor的執行規則也瞭解完了,那麼接下來我們就來介紹3種常見的執行緒池,它們都直接或者間接地通過配置ThreadPoolExecutor來實現自己的功能特性,這個3種執行緒池分別是FixedThreadPool,CachedThreadPool,ScheduledThreadPool以及SingleThreadExecutor。2.1FixedThreadPool FixedThreadPool模式會使用一個優先固定數目的執行緒來處理若干數目的任務。規定數目的執行緒處理所有任務,一旦有執行緒處理完了任務就會被用來處理新的任務(如果有的話)。FixedThreadPool模式下最多的執行緒數目是一定的。建立FixedThreadPool物件程式碼如下:
ExecutorService fixedThreadPool=Executors.newFixedThreadPool(5);
我們來看看FixedThreadPool建立方法原始碼:
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
FixedThreadPool的corePoolSize和maximumPoolSize引數都被設定為nThreads。當執行緒池中的執行緒數量大於corePoolSize時,keepAliveTime為非核心空閒執行緒等待新任務的最長時間,超過這個時間後非核心執行緒將被終止,這裡keepAliveTime設定為0L,就說明非核心執行緒會立即被終止。事實上這裡也沒有非核心執行緒建立,因為核心執行緒數和最大執行緒數都一樣的。下面我們來看看FixedThreadPool的execute()方法的執行流程:


分析:(1)如果當前執行執行緒數少corePoolSize,則建立一個新的執行緒來執行任務。(2)如果當前執行緒池的執行執行緒數等於corePoolSize,那麼後面提交的任務將加入LinkedBlockingQueue。(3)執行緒在執行完圖中的1後,會在迴圈中反覆從LinkedBlockingQueue獲取任務來執行。這裡還有點要說明的是FixedThreadPool使用的是無界佇列LinkedBlockingQueue作為執行緒池的工作佇列(佇列容量為Integer.MAX_VALUE)。使用該佇列作為工作佇列會對執行緒池產生如下影響(1)當前執行緒池中的執行緒數量達到corePoolSize後,新的任務將在無界佇列中等待。(2)由於我們使用的是無界佇列,所以引數maximumPoolSize和keepAliveTime無效。(3)由於使用無界佇列,執行中的FixedThreadPool不會拒絕任務(當然此時是未執行shutdown和shutdownNow方法),所以不會去呼叫RejectExecutionHandler的rejectExecution方法丟擲異常。下面我們給出案例,該案例來自java程式設計思想一書:
public class LiftOff implements Runnable{   
    protected int countDown = 10; //Default   
    private static int taskCount = 0;   
    private final int id = taskCount++;    
    public LiftOff() {}   
    public LiftOff(int countDown) {   
        this.countDown = countDown;   
    }   
    public String status() {   
        return "#" + id + "(" +   
            (countDown > 0 ? countDown : "LiftOff!") + ") ";   
    }   
    @Override   
    public void run() {   
        while(countDown-- > 0) {   
            System.out.print(status());   
            Thread.yield();   
        }   
           
    }      
} 
宣告一個Runnable物件,使用FixedThreadPool執行任務如下:
public class FixedThreadPool {   
    public static void main(String[] args) {  
        //三個執行緒來執行五個任務   
        ExecutorService exec = Executors.newFixedThreadPool(3);      
        for(int i = 0; i < 5; i++) {   
            exec.execute(new LiftOff());   
        }  
        exec.shutdown();   
    }   
}  
2.2 CachedThreadPool
CachedThreadPool首先會按照需要建立足夠多的執行緒來執行任務(Task)。隨著程式執行的過程,有的執行緒執行完了任務,可以被重新迴圈使用時,才不再建立新的執行緒來執行任務。建立方式:
ExecutorService cachedThreadPool=Executors.newCachedThreadPool(); 
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
從該靜態方法,我們可以看到CachedThreadPool的corePoolSize被設定為0,而maximumPoolSize被設定Integer.MAX_VALUE,即maximumPoolSize是無界的,而keepAliveTime被設定為60L,單位為妙。也就是空閒執行緒等待時間最長為60秒,超過該時間將會被終止。而且在這裡CachedThreadPool使用的是沒有容量的SynchronousQueue作為執行緒池的工作佇列,但其maximumPoolSize是無界的,也就是意味著如果主執行緒提交任務的速度高於maximumPoolSize中執行緒處理任務的速度時CachedThreadPool將會不斷的建立新的執行緒,在極端情況下,CachedThreadPool會因為建立過多執行緒而耗盡CPU和記憶體資源。CachedThreadPool的execute()方法的執行流程:


分析:(1)首先執行SynchronousQueue.offer(Runnable task),新增一個任務。如果當前CachedThreadPool中有空閒執行緒正在執行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS),其中NANOSECONDS是毫微秒即十億分之一秒(就是微秒/1000),那麼主執行緒執行offer操作與空閒執行緒執行poll操作配對成功,主執行緒把任務交給空閒執行緒執行,execute()方法執行完成,否則進入第(2)步。(2)當CachedThreadPool初始執行緒數為空時,或者當前沒有空閒執行緒,將沒有執行緒去執行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。這樣的情況下,步驟(1)將會失敗,此時CachedThreadPool會建立一個新的執行緒來執行任務,execute()方法執行完成。(3)在步驟(2)中建立的新執行緒將任務執行完成後,會執行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS),這個poll操作會讓空閒執行緒最多在SynchronousQueue中等待60秒,如果60秒內主執行緒提交了一個新任務,那麼這個空閒執行緒將會執行主執行緒提交的新任務,否則,這個空閒執行緒將被終止。由於空閒60秒的空閒執行緒會被終止,因此長時間保持空閒的 CachedThreadPool是不會使用任何資源的。根據前面的分析我們知道SynchronousQueue是一個沒有容量的阻塞佇列(其實個人認為是相對應時間而已的沒有容量,因為時間到空閒執行緒就會被移除)。每個插入操作必須等到一個執行緒與之對應。CachedThreadPool使用SynchronousQueue,把主執行緒的任務傳遞給空閒執行緒執行。流程如下:
CachedThreadPool使用的案例程式碼如下:
public class CachedThreadPool {   
    public static void main(String[] args) {   
        ExecutorService exec = Executors.newCachedThreadPool();   
        for(int i = 0; i < 10; i++) {   
            exec.execute(new LiftOff());   
        }   
        exec.shutdown();       
    }   
}   
2.3 SingleThreadExecutor

SingleThreadExecutor模式只會建立一個執行緒。它和FixedThreadPool比較類似,不過執行緒數是一個。如果多個任務被提交給SingleThreadExecutor的話,那麼這些任務會被儲存在一個佇列中,並且會按照任務提交的順序,一個先執行完成再執行另外一個執行緒。SingleThreadExecutor模式可以保證只有一個任務會被執行。這種特點可以被用來處理共享資源的問題而不需要考慮同步的問題。

建立方式:
ExecutorService singleThreadExecutor=Executors.newSingleThreadExecutor(); 
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
從靜態方法可以看出SingleThreadExecutor的corePoolSize和maximumPoolSize被設定為1,其他引數則與FixedThreadPool相同。SingleThreadExecutor使用的工作佇列也是無界佇列LinkedBlockingQueue。由於SingleThreadExecutor採用無界佇列的對執行緒池的影響與FixedThreadPool一樣,這裡就不過多描述了。同樣的我們先來看看其執行流程:


分析:(1)如果當前執行緒數少於corePoolSize即執行緒池中沒有執行緒執行,則建立一個新的執行緒來執行任務。(2)線上程池的執行緒數量等於corePoolSize時,將任務加入到LinkedBlockingQueue。(3)執行緒執行完成(1)中的任務後,會在一個無限迴圈中反覆從LinkedBlockingQueue獲取任務來執行。SingleThreadExecutor使用的案例程式碼如下:
public class SingleThreadExecutor {   
    public static void main(String[] args) {   
        ExecutorService exec = Executors.newSingleThreadExecutor();   
        for (int i = 0; i < 2; i++) {   
            exec.execute(new LiftOff());   
        }   
    }   
}  
2.4 各自的適用場景
FixedThreadPool:適用於為了滿足資源管理需求,而需要限制當前執行緒的數量的應用場景,它適用於負載比較重的伺服器。
SingleThreadExecutor:適用於需要保證執行順序地執行各個任務;並且在任意時間點,不會有多個執行緒是活動的場景。CachedThreadPool:大小無界的執行緒池,適用於執行很多的短期非同步任務的小程式,或者負載較輕的伺服器。3.ScheduledThreadPoolExecutor淺析 
3.1 ScheduledThreadPoolExecutor執行機制分析
ScheduledThreadPoolExecutor繼承自ThreadPoolExecutor。它主要用來在給定的延遲之後執行任務,或者定期執行任務。ScheduledThreadPoolExecutor的功能與Timer類似,但比Timer更強大,更靈活,Timer對應的是單個後臺執行緒,而ScheduledThreadPoolExecutor可以在建構函式中指定多個對應的後臺執行緒數。接下來我們先來了解一下ScheduledThreadPoolExecutor的執行機制:

分析:DelayQueue是一個無界佇列,所以ThreadPoolExecutor的maximumPoolSize在ScheduledThreadPoolExecutor中無意義。ScheduledThreadPoolExecutor的執行主要分為以下兩個部分(1)當呼叫ScheduledThreadPoolExecutor的scheduleAtFixedRate()方法或者scheduleWithFixedDelay()方法時,會向ScheduledThreadPoolExecutor的DelayQueue新增一個實現了RunnableScheduledFuture介面的ScheduleFutureTask。(2)執行緒池中的執行緒從DelayQueue中獲取ScheduleFutureTask,然後執行任務。3.2 如何建立ScheduledThreadPoolExecutor?ScheduledThreadPoolExecutor通常使用工廠類Executors來建立,Executors可以建立兩種型別的ScheduledThreadPoolExecutor,如下:
(1)ScheduledThreadPoolExecutor:可以執行並行任務也就是多條執行緒同時執行。(2)SingleThreadScheduledExecutor:可以執行單條執行緒。建立ScheduledThreadPoolExecutor的方法構造如下:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
建立SingleThreadScheduledExecutor的方法構造如下:
public static ScheduledExecutorService newSingleThreadScheduledExecutor()
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)

建立例項物件程式碼如下:

ScheduledExecutorService scheduledThreadPoolExecutor=Executors.newScheduledThreadPool(5);
ScheduledExecutorService singleThreadScheduledExecutor=Executors.newSingleThreadScheduledExecutor();
3.3 ScheduledThreadPoolExecutor和SingleThreadScheduledExecutor的適用場景
ScheduledThreadPoolExecutor:適用於多個後臺執行緒執行週期性任務,同時為了滿足資源管理的需求而需要限制後臺執行緒數量的應用場景。
SingleThreadScheduledExecutor:適用於需要單個後臺執行緒執行週期任務,同時需要保證任務順序執行的應用場景。
3.4 ScheduledThreadPoolExecutor使用案例
我們建立一個Runnable的物件,然後使用ScheduledThreadPoolExecutor的Scheduled()來執行延遲任務,輸出執行時間即可:
我們先來介紹一下該類延遲執行的方法:
public ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);
引數解析:command:就是一個實現Runnable介面的類delay:延遲多久後執行。unit:用於指定keepAliveTime引數的時間單位,這是一個列舉,常用的有TimeUnit.MILLISECONDS(毫秒),TimeUnit.SECONDS(秒)以及TimeUnit.MINUTES(分鐘)等。這裡要注意這個方法會返回ScheduledFuture例項,可以用於獲取執行緒狀態資訊和延遲時間。
package com.zejian.Executor;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
 * @author zejian
 * @time 2016年3月14日 下午9:10:41
 * @decrition 建立一個工作執行緒繼承Runnable
 */
public class WorkerThread implements Runnable{
	@Override
	public void run() {
		 System.out.println(Thread.currentThread().getName()+" Start. Time = "+getNowDate());
	     threadSleep();
	     System.out.println(Thread.currentThread().getName()+" End. Time = "+getNowDate());
		
	}
	/**
	 * 睡3秒
	 */
	public void threadSleep(){
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	 /**
	  * 獲取現在時間
	  * 
	  * @return 返回時間型別 yyyy-MM-dd HH:mm:ss
	  */
	public static String getNowDate() {
		  Date currentTime = new Date();
		  SimpleDateFormat formatter; 
		    formatter = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss"); 
		    String ctime = formatter.format(currentTime); 
		  return ctime;
		 }
}
執行類如下:
package com.zejian.Executor;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
 * @author zejian
 * @time 2016年3月14日 下午9:27:06
 * @decrition 執行類
 */
public class ScheduledThreadPoolTest {
	
	public static void main(String[] args) {
		ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
		 try {
	    //schedule to run after sometime
	    System.out.println("Current Time = "+getNowDate());
	    for(int i=0; i<3; i++){
	        Thread.sleep(1000);
	        WorkerThread worker = new WorkerThread();
	        //延遲10秒後執行
	        scheduledThreadPool.schedule(worker, 10, TimeUnit.SECONDS);
	    }
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	    scheduledThreadPool.shutdown();
	    while(!scheduledThreadPool.isTerminated()){
	        //wait for all tasks to finish
	    }
	    System.out.println("Finished all threads");
	}
	
	 /**
	  * 獲取現在時間
	  * 
	  * @return 返回時間型別 yyyy-MM-dd HH:mm:ss
	  */
	 public static String getNowDate() {
	  Date currentTime = new Date();
	  SimpleDateFormat formatter; 
	    formatter = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss"); 
	    String ctime = formatter.format(currentTime); 
	  return ctime;
	 }
}
執行輸入執行結果:


執行緒任務確實在10秒延遲後才開始執行。這就是schedule()方法的使用。下面我們再介紹2個可用於週期性執行任務的方法。

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)

scheduleAtFixedRate方法的作用是預定在初始的延遲結束後,週期性地執行給定的任務,週期長度為period,其中initialDelay為初始延遲。

按照固定的時間來執行,即:到點執行

 public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);
scheduleWithFixedDelay方法的作用是預定在初始的延遲結束後周期性地執行給定任務,在一次呼叫完成和下一次呼叫開始之間有長度為delay的延遲,其中initialDelay為初始延遲(簡單說是是等上一個任務結束後,在等固定的時間,然後執行。即:執行完上一個任務後再執行)。下面給出實現案例程式碼參考:
package com.zejian.Executor;
import java.util.Date;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
 * @author zejian
 * @time 2016年3月14日 下午10:05:07
 * @decrition 周期函式測試類
 */
public class ScheduledTask {
	public ScheduledThreadPoolExecutor se = new ScheduledThreadPoolExecutor(5);
	public static void main(String[] args) {
		new ScheduledTask();
	}
	public void fixedPeriodSchedule() {
		// 設定可以迴圈執行的runnable,初始延遲為0,這裡設定的任務的間隔為5秒
		for(int i=0;i<5;i++){
			se.scheduleAtFixedRate(new FixedSchedule(), 0, 5, TimeUnit.SECONDS);
		}
	}
	public ScheduledTask() {
		fixedPeriodSchedule();
	}
	class FixedSchedule implements Runnable {
		public void run() {
			System.out.println("當前執行緒:"+Thread.currentThread().getName()+"  當前時間:"+new Date(System.currentTimeMillis()));
		}
	}
}
執行結果(後來補貼的結果,所以時間是2017)
當前執行緒:pool-1-thread-5  當前時間:Tue Aug 08 09:43:18 CST 2017
當前執行緒:pool-1-thread-4  當前時間:Tue Aug 08 09:43:18 CST 2017
當前執行緒:pool-1-thread-3  當前時間:Tue Aug 08 09:43:18 CST 2017
當前執行緒:pool-1-thread-1  當前時間:Tue Aug 08 09:43:18 CST 2017
當前執行緒:pool-1-thread-2  當前時間:Tue Aug 08 09:43:18 CST 2017
當前執行緒:pool-1-thread-1  當前時間:Tue Aug 08 09:43:23 CST 2017
當前執行緒:pool-1-thread-4  當前時間:Tue Aug 08 09:43:23 CST 2017
當前執行緒:pool-1-thread-3  當前時間:Tue Aug 08 09:43:23 CST 2017
當前執行緒:pool-1-thread-5  當前時間:Tue Aug 08 09:43:23 CST 2017
當前執行緒:pool-1-thread-2  當前時間:Tue Aug 08 09:43:23 CST 2017
當前執行緒:pool-1-thread-1  當前時間:Tue Aug 08 09:43:28 CST 2017
當前執行緒:pool-1-thread-4  當前時間:Tue Aug 08 09:43:28 CST 2017
當前執行緒:pool-1-thread-5  當前時間:Tue Aug 08 09:43:28 CST 2017
當前執行緒:pool-1-thread-3  當前時間:Tue Aug 08 09:43:28 CST 2017
當前執行緒:pool-1-thread-1  當前時間:Tue Aug 08 09:43:28 CST 2017

至於scheduleWithFixedDelay方法,大家就把程式碼稍微修改一下執行試試就行,這裡就不重複了。而SingleThreadScheduledExecutor的使用的方法基本是類似,只不過是單執行緒罷了,這裡也不再描述了。好了,今天就到這吧。
主要參考書籍:

java核心技術卷1

android開發藝術探索

java併發程式設計的藝術