[轉]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使用的案例代碼如下:

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{@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博客文章一鍵轉載插件

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/285095.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/285095.shtml
英文地址,請注明出處:http://en.pswp.cn/news/285095.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C#實現清理系統內存

金山內存整理工具、360內存清理工具非常好用&#xff0c;可以將系統內存最小化&#xff0c;提升系統運行速度。其實這些事情C#也可以做到&#xff0c;原理就是對系統進程中的進程內存進行逐個優化。 網上大多推薦使用系統的SetProcessWorkingSetSize的函數API&#xff0c;但是經…

C語言試題168之獲取矩陣的最大值及其下標

??個人主頁:個人主頁 ??系列專欄:C語言試題200例 ??推薦一款刷算法、筆試、面經、拿大公司offer神器?? 點擊跳轉進入網站 ?作者簡介:大家好,我是碼莎拉蒂,CSDN博客專家(全站排名Top 50),阿里云博客專家、51CTO博客專家、華為云享專家 1、題目 題目:要求使用…

.Net下極限生產力之efcore分表分庫全自動化遷移CodeFirst

開始本次我們的主題就是極限生產力,其他語言望塵莫及的分表分庫全自動化Migrations Code-First 加 efcore 分表分庫無感開發還記得上次發布博客還是在上次,上次發布了如何兼容WTM框架后也有不少小伙伴來問我如何兼容如何遷移等問題,經過這么多框架的兼容我自己也認識到了一些問…

Hadoop日常管理與維護

本文描述了hadoop、hbase的啟動關閉、表操作以及權限管理。一、Hadoop服務的啟動與關閉1、啟動使用hadoop以及hbase自帶的腳本進行啟動&#xff0c;先啟動hadoop個服務&#xff0c;再啟動hbase服務。 hadoopbdi:~$ start-dfs.sh hadoopbdi:~$ start-yarn.sh hadoopbdi:~$ start…

Mathematica修改默認字體

1. 打開Option Inspector 2. 第一個下拉框選擇Global Preference, 搜索stylehints 3. 修改字體為想要換的字體FamilyName, 比如換成蘋果黑體 SimHei, 字體FamilyName自行研究 4. 效果 轉載于:https://www.cnblogs.com/dabaopku/p/6221960.html

基于JavaScript 數組的經典程序應用源碼(強烈建議收藏)

文章目錄設計一個數組輸入并顯示的程序。數組輸入和顯示選擇排序選擇排序排序程序包排序網頁楊輝三角形楊輝三角形網頁C語言畫一個sin(x)的曲線螺旋線訪問二維數組JavaScript數組的定義、使用都是非常簡單的&#xff0c;僅僅定義的話&#xff0c;就使用&#xff1a; var anew …

C語言試題169之誰家孩子跑得最慢

??個人主頁:個人主頁 ??系列專欄:C語言試題200例 ??推薦一款刷算法、筆試、面經、拿大公司offer神器?? 點擊跳轉進入網站 ?作者簡介:大家好,我是碼莎拉蒂,CSDN博客專家(全站排名Top 50),阿里云博客專家、51CTO博客專家、華為云享專家 1、題目 題目:張、王、…

【ArcGIS微課1000例】0008:ArcGIS中如何設置相對路徑?(解決圖層前紅色的感嘆號)

文章目錄 問題舉例問題分析解決辦法問題舉例 我們在使用ArcGIS時經常會碰到這樣的問題:將地圖文檔(.mxd)保存到本地并拷貝到別的電腦上或改變一個路徑時,出現數據丟失的現象,具體表現為圖層前面出現一個紅色的感嘆號,如下圖所示。 出現以上問題的根本原因是數據GSS.tif的…

IPC-----消息隊列

消息隊列&#xff08;報文隊列&#xff09;:兩個進程間通過發送數據塊的形式進行通信。一個進程把需要發送的消息通過一個函數發送到消息隊列中&#xff0c;另一個進程再從消息隊列中讀取該消息。函數&#xff1a;# include <sys/types.h># include <sys/ipc.h>key…

[轉]OKR結合CFR的管理模式

讀前預 無論任何管理書籍&#xff0c;都是圍繞著人性&#xff0c;如果激發員工的人性中的自尊和自我價值觀、自我成就感。 作為一名領導者&#xff0c;在管理前&#xff0c;必須要是冷靜&#xff0c;安靜的對待他人 約翰杜爾為谷歌送上大禮 “好主意”再加上”卓越的執行”&…

NetCore 國際化最佳實踐

NetCore 國際化最佳實踐ASP.NET Core中提供了一些本地化服務和中間件&#xff0c;可將網站本地化為不同的語言文化。ASP.NET Core中我們可以使用Microsoft.AspNetCore.Localization庫來實現本地化。但是默認只支持使用資源文件方式做多語言存儲&#xff0c;很難在實際場景中使用…

復分析——第1章——復分析準備知識(E.M. Stein R. Shakarchi)

第一章 復分析準備知識 (Preliminaries to Complex Analysis) The sweeping development of mathematics during the last two centuries is due in large part to the introduction of complex numbers; paradoxically, this is based on the seemingly absurd no…

網構軟件-Internetware

網構軟件&#xff0c;973項目提的一個概念。概念。作為一種新型軟件形態&#xff0c;它應該涵蓋哪些研究范疇&#xff1f;評價它成功的基本標準是什么&#xff1f;我們看待一件事物&#xff0c;首先都會從已知的認知空間里尋找與之對應者。那么&#xff0c;能與網構相對應的大概…

List集合的remove一個對象的方法

import java.util.ArrayList;import java.util.List;class A{   public boolean equals(Object obj){     return true;   }} public class ListTest2 { public static void main(String[] args) {   // TODO Auto-generated method stub     System.out.println…

【ArcGIS微課1000例】0009:ArcGIS影像拼接(鑲嵌、鑲嵌至新柵格)

本課程以 DEM數據為例,講述ArcGIS中影像的拼接方法及注意事項。 文章目錄 方法一:Mosaic工具方法二:Mosaic To New Raster工具實驗數據下載觀察兩個數據,接邊處切合效果很好。 方法一:Mosaic工具 使用Mosaic工具要千萬注意,Mosaic的結果是將輸入柵格追加到目標柵格上,…

Python:file (read,readline,readline )使用方法

Python讀取文件時&#xff0c;在使用readlin、readlines時會有疑惑&#xff0c;下面給大家詳解&#xff1a;一、例&#xff1a;a.txt的內容為 aaa 123 bbb 456二、首先我先設置個變量&#xff1a; a"a.txt" cfile(a)三、此時我們分別看下使用read、readl…

C語言試題170之矩陣轉置

??個人主頁:個人主頁 ??系列專欄:C語言試題200例 ??推薦一款刷算法、筆試、面經、拿大公司offer神器?? 點擊跳轉進入網站 ?作者簡介:大家好,我是碼莎拉蒂,CSDN博客專家(全站排名Top 50),阿里云博客專家、51CTO博客專家、華為云享專家 1、題目 題目:設有一矩…

【ArcGIS微課1000例】0010:ArcGIS影像裁剪(裁剪、掩膜提取)

文章目錄 裁剪方法方法一:Extract By Mask(按掩膜提取)方法二:Clip(裁剪)數據下載裁剪方法 方法一:Extract By Mask(按掩膜提取) 加載配套的實驗數據,運行Extract By Mask(按掩膜提取)工具,參數設置如下: 掩膜提取結果: 方法二:Clip(裁剪) 加載配套的實驗…

阿里創新自動化測試工具平臺--Doom

背景 信息系統上線后通常會需要迭代升級甚至重構&#xff0c;如何確保系統原有業務的正確性非常重要。曾經有一家叫瑞穗證券的證券公司因為一個系統bug導致了數億美金的損失&#xff0c;賠掉了公司一年的利潤。這樣的極端例子雖然少見&#xff0c;但是卻像達摩克利斯之劍警示著…

《微信讀書》自定義樣式

一直用微信讀書看書&#xff0c;但是微信讀書的Web版的布局不太喜歡。 重寫下它的樣式再加上單擊關閉工具欄&#xff0c;這樣看著舒服多了^_^&#xff1a; /*浮動工具欄*/ document.getElementsByClassName("readerControls")[0].style.left"0"; document.…