[转]javaandroid线程池
?
java多線程-概念&創建啟動&中斷&守護線程&優先級&線程狀態(多線程編程之一)
java多線程同步以及線程間通信詳解&消費者生產者模式&死鎖&Thread.join()(多線程編程之二)
java&android線程池-Executor框架之ThreadPoolExcutor&ScheduledThreadPoolExecutor淺析(多線程編程之三)
Java多線程:Callable、Future和FutureTask淺析(多線程編程之四)
?
無論是在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使用的案例代碼如下:
?
2.3 SingleThreadExecutorSingleThreadExecutor模式只會創建一個線程。它和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{@Overridepublic 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 blocke.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 sometimeSystem.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并發編程的藝術
---------------------
作者:zejian_
來源:CSDN
原文:https://blog.csdn.net/javazejian/article/details/50890554
版權聲明:本文為作者原創文章,轉載請附上博文鏈接!
內容解析By:CSDN,CNBLOG博客文章一鍵轉載插件
總結
以上是生活随笔為你收集整理的[转]javaandroid线程池的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [转]ES7、ES8、ES9、ES10新
- 下一篇: [转]JS导出PDF