📖摘要
今天分享下 —— 面試:講一講Java併發-Executor? 的一些基本知識,歡迎關注!
🌂分享:Executor
框架
😀Executor
接口
public interface Executor {
void execute(Runnable command);
}
Executor
接口是Executor
框架中最基礎的部分,定義了一個用於執行Runnable
的execute
方法,它沒有實現類只有另一個重要的子接口ExecutorService
😁ExecutorService
接口
//繼承自Executor接口
public interface ExecutorService extends Executor {
/**
* 關閉方法,調用後執行之前提交的任務,不再接受新的任務
*/
void shutdown();
/**
* 從語義上可以看出是立即停止的意思,將暫停所有等待處理的任務並返回這些任務的列表
*/
List<Runnable> shutdownNow();
/**
* 判斷執行器是否已經關閉
*/
boolean isShutdown();
/**
* 關閉後所有任務是否都已完成
*/
boolean isTerminated();
/**
* 中斷
*/
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
/**
* 提交一個Callable任務
*/
<T> Future<T> submit(Callable<T> task);
/**
* 提交一個Runable任務,result要返回的結果
*/
<T> Future<T> submit(Runnable task, T result);
/**
* 提交一個任務
*/
Future<?> submit(Runnable task);
/**
* 執行所有給定的任務,當所有任務完成,返回保持任務狀態和結果的Future列表
*/
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
/**
* 執行給定的任務,當所有任務完成或超時期滿時(無論哪個首先發生),返回保持任務狀態和結果的 Future 列表。
*/
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
/**
* 執行給定的任務,如果某個任務已成功完成(也就是未拋出異常),則返回其結果。
*/
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
/**
* 執行給定的任務,如果在給定的超時期滿前某個任務已成功完成(也就是未拋出異常),則返回其結果。
*/
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
ExecutorService
接口繼承自Executor
接口,定義了終止、提交、執行任務、跟蹤任務返回結果等方法。
ExecutorService
的生命週期有三種狀態:運行、關閉和已終止。
- execute(Runnable command):履行
Ruannable
類型的任務, - submit(task):可用來提交
Callable
或Runnable
任務,並返回代表此任務的Future
對象 - shutdown():在完成已提交的任務後封閉辦事,不再接管新任務,
- shutdownNow():停止所有正在履行的任務並封閉辦事。
- isTerminated():測試是否所有任務都履行完畢了。,
- isShutdown():測試是否該
ExecutorService
已被關閉
😂工具類 Executors
的靜態方法
負責生成各種類型的 ExecutorService
線程池實例,共有四種。
newFixedThreadPool(numberOfThreads:int)
:(固定線程池)ExecutorService 創建一個固定線程數量的線程池,並行執行的線程數量不變,線程當前任務完成後,可以被重用執行另一個任務newCachedThreadPool()
:(可緩存線程池)ExecutorService
創建一個線程池,按需創建新線程,如果線程的當前規模超過了處理需求時,阿麼將回收空閒的線程,而當需求增加時,則可以添加新的線程,線程池的規模不存在任何限制new SingleThreadExecutor()
;(單線程執行器)線程池中只有一個線程,依次執行任務new ScheduledThreadPool()
:線程池按時間計劃來執行任務,允許用戶設定執行任務的時間,類似於timer
🤣Runnable、Callable、Future接口
Runnable接口
// 實現Runnable接口的類將被Thread執行,表示一個基本的任務
public interface Runnable {
// run方法就是它所有的內容,就是實際執行的任務
public abstract void run();
}
Callable接口
與
Runnable
接口的區別在於它接收泛型,同時它執行任務後帶有返回內容
// Callable同樣是任務,與Runnable接口的區別在於它接收泛型,同時它執行任務後帶有返回內容
public interface Callable<V> {
// 相對於run方法的帶有返回值的call方法
V call() throws Exception;
}
Runnable
接口和Callable
接口的實現類,都可以被ThreadPoolExecutor
和ScheduledThreadPoolExecutor
執行,他們之間的區別是Runnable
不會返回結果,而Callable
可以返回結果。
Executors
可以把一個 Runnable
對象轉換成 Callable
對象:
public static Callable<Object> callable(Runnbale task);
當把一個 Callable
對象 (Callable1,Callable2)
提交給 ThreadPoolExecutor
和 ScheduledThreadPoolExecutor
執行時,submit(...)
會向我們返回一個 FutureTask
對象。我們執行 FutureTask.get()
來等待任務執行完成,當任務完成後, FutureTask.get()
將返回任務的結果。
Future接口
// Future代表異步任務的執行結果
public interface Future<V> {
/**
* 嘗試取消一個任務,如果這個任務不能被取消(通常是因爲已經執行完了),返回false,否則返回true。
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
* 返回代表的任務是否在完成之前被取消了
*/
boolean isCancelled();
/**
* 如果任務已經完成,返回true
*/
boolean isDone();
/**
* 獲取異步任務的執行結果(如果任務沒執行完將等待)
*/
V get() throws InterruptedException, ExecutionException;
/**
* 獲取異步任務的執行結果(有最常等待時間的限制)
*
* timeout表示等待的時間,unit是它時間單位
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Future
就是對於具體的Runnable
或者Callable
任務的執行結果進行取消、查詢是否完成、獲取結果。必要時可以通過get
方法獲取執行結果,該方法會阻塞直到任務返回結果
- cancel方法用來取消任務,如果取消任務成功則返回true,如果取消任務失敗則返回false。
- isCancelled方法表示任務是否被取消成功,如果在任務正常完成前被取消成功,則返回 true。
- isDone方法表示任務是否已經完成,若任務完成,則返回true;
- get()方法用來獲取執行結果,這個方法會產生阻塞,會一直等到任務執行完畢才返回;
- get(long timeout, TimeUnit unit)用來獲取執行結果,如果在指定時間內,還沒獲取到結果,就直接返回null。
也就是說Future提供了三種功能:
- 判斷任務是否完成;
- 能夠中斷任務;
- 能夠獲取任務執行結果。
線程池的處理流程
線程池優先要創建出基本線程池大小(corePoolSize)的線程數量,沒有達到這個數量時,每次提交新任務都會直接創建一個新線程,當達到了基本線程數量後,又有新任務到達,優先放入等待隊列,如果隊列滿了,纔去創建新的線程(不能超過線程池的最大數maxmumPoolSize)。
😃向線程池提交任務的兩種方式
通過execute()
方法
ExecutorService threadpool= Executors.newFixedThreadPool(10); threadpool.execute(new Runnable(){...});
這種方式提交沒有返回值,也就不能判斷任務是否被線程池執行成功。
通過submit()
方法
Future<?> future = threadpool.submit(new Runnable(){...});
try {
Object res = future.get();//獲取任務執行結果
} catch (InterruptedException e) {
// 處理中斷異常
e.printStackTrace();
} catch (ExecutionException e) {
// 處理無法執行任務異常
e.printStackTrace();
}finally{
// 關閉線程池
executor.shutdown();
}
使用
submit
方法來提交任務,它會返回一個Future
對象,通過future
的get
方法來獲取返回值,get
方法會阻塞住直到任務完成,而使用get(long timeout, TimeUnit unit)
方法則會阻塞一段時間後立即返回,這時有可能任務沒有執行完。
線程池本身的狀態
volatile int runState;
static final int RUNNING = 0; //運行狀態
static final int SHUTDOWN = 1; //關閉狀態
static final int STOP = 2; //停止
static final int TERMINATED = 3; //終止,終結
- 當創建線程池後,初始時,線程池處於
RUNNING
狀態; - 如果調用了
shutdown()
方法,則線程池處於SHUTDOWN
狀態,此時線程池不能夠接受新的任務,它會等待所有任務執行完畢,最後終止; - 如果調用了
shutdownNow()
方法,則線程池處於STOP
狀態,此時線程池不能接受新的任務,並且會去嘗試終止正在執行的任務,返回沒有執行的任務列表; - 當線程池處於
SHUTDOWN
或STOP
狀態,並且所有工作線程已經銷燬,任務緩存隊列已經清空或執行結束後,線程池被設置爲TERMINATED
狀態。
😄ForkJoinPool
ForkJoinPool
是jdk1.7新引入的線程池,基於 ForkJoin
框架,使用了“分治”的思想。關於ForkJoin框架參考另一篇筆記“Java併發之J.U.C”。
ThreadPoolExecutor
中每個任務都是由單個線程獨立處理的,如果出現一個非常耗時的大任務(比如大數組排序),就可能出現線程池中只有一個線程在處理這個大任務,而其他線程卻空閒着,這會導致CPU負載不均衡:空閒的處理器無法幫助工作繁忙的處理器。
ForkJoinPool
就是用來解決這種問題的:將一個大任務拆分成多個小任務後,使用 fork
可以將小任務分發給其他線程同時處理,使用 join
可以將多個線程處理的結果進行彙總;這實際上就是分治思想的並行版本。
😅ForkJoinPool
基本原理
ForkJoinPool
類是Fork/Join
框架的核心,和ThreadPoolExecutor
一樣它也是ExecutorService
接口的實現類。
所以上面的線程池框架圖是比較老的了,新的框架圖如下圖所示:
🎉最後
-
更多參考精彩博文請看這裏:《陳永佳的博客》
-
喜歡博主的小夥伴可以加個關注、點個贊哦,持續更新嘿嘿!