不忘初心 砥礪前行, Tomorrow Is Another Day !
相關文章
本文概要:
- 認識Executor與ExecutorService
- 理解Future與FuturTask
- ThreadPoolExecutor介紹
- Executors工廠類
執行緒池的優點:
- 重用執行緒,避免不必要的物件建立和銷燬.
- 可有效控制最大併發執行緒數,提高系統資源的使用率.避免因執行緒過多強佔系統資源導致阻塞.
一. 認識Executor與ExecutorService
- 認識Executor
Executor作為執行緒池的頂級介面. 在Java的設計中,Runnable負責任務的提交. Executor負責任務的執行.將任務進行了解耦.
public interface Executor {
void execute(Runnable command);//執行已提交的 Runnable 任務物件
}
複製程式碼
- 認識ExecutorService
ExecutorService介面繼承了Executor介面,定義了一些生命週期的方法
public interface ExecutorService extends Executor {
void shutdown();//順次地關閉ExecutorService,停止接收新的任務,等待所有已經提交的任務執行完畢之後,關閉ExecutorService
List<Runnable> shutdownNow();//阻止等待任務啟動並試圖停止當前正在執行的任務,停止接收新的任務,返回處於等待的任務列表
boolean isShutdown();//判斷執行緒池是否已經關閉
boolean isTerminated();//如果關閉後所有任務都已完成,則返回 true。注意,除非首先呼叫 shutdown 或 shutdownNow,否則 isTerminated 永不為 true。
boolean awaitTermination(long timeout, TimeUnit unit)//等待(阻塞)直到關閉或最長等待時間或發生中斷,timeout - 最長等待時間 ,unit - timeout 引數的時間單位 如果此執行程式終止,則返回 true;如果終止前超時期滿,則返回 false
<T> Future<T> submit(Callable<T> task);//提交一個返回值的任務用於執行,返回一個表示任務的未決結果的 Future。該 Future 的 get 方法在成功完成時將會返回該任務的結果。
<T> Future<T> submit(Runnable task, T result);//提交一個 Runnable 任務用於執行,並返回一個表示該任務的 Future。該 Future 的 get 方法在成功完成時將會返回給定的結果。
Future<?> submit(Runnable task);//提交一個 Runnable 任務用於執行,並返回一個表示該任務的 Future。該 Future 的 get 方法在成功 完成時將會返回 null
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)//執行給定的任務,當所有任務完成時,返回保持任務狀態和結果的 Future 列表。返回列表的所有元素的 Future.isDone() 為 true。
throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)//執行給定的任務,當所有任務完成時,返回保持任務狀態和結果的 Future 列表。返回列表的所有元素的 Future.isDone() 為 true。
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中運用到了Future相關知識,下面對Futue做一個簡單瞭解.
二. 理解Future與FutureTask
- 理解Future
Future簡單理解就是對非同步任務的統計類,包含進行取消、查詢是否完成、獲取結果等操作.
Future類位於java.util.concurrent包下,對應原始碼.
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
//表示如果在任務完成前被取消成功,則返回true
boolean isCancelled();
////表示任務執行結束,無論是正常結束/中斷/發生異常,都返回true
boolean isDone();
//用來獲取執行結果,這個方法會產生阻塞,會一直等到任務執行完畢才返回
V get() throws InterruptedException, ExecutionException;
//用來獲取執行結果,有超時機制,如果阻塞時間超過了指定時間,會丟擲異常
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
複製程式碼
- 理解FutureTask
FutureTask既實現了Future介面,又實現了Runnable介面.
對應原始碼
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
//構造方法
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//回撥callable的Call方法,獲取非同步任務返回值.
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
//...省略部分程式碼
}
}
複製程式碼
所以我們可以通過Runnable介面實現執行緒,又可以通過Future介面獲取執行緒執行完後的結果.
使用示例 上一篇文章Thread基礎中已經使用過Future了,這裡直接換成FutureTask的使用.
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("子執行緒正在幹活");
Thread.sleep(3000);
return "實現Callable介面,重寫Call方法";
}
public static void main(String[] args) throws Exception {
useFutureTask();
}
private static void useFutureTask() throws InterruptedException, ExecutionException {
MyCallable myCallable = new MyCallable();
ExecutorService executorService = Executors.newSingleThreadExecutor();
//定義一個Task,再提交任務.
FutureTask<String> futureTask = new FutureTask<>(myCallable);
executorService.submit(futureTask);
executorService.shutdown();
Thread.sleep(1000);//模擬正在幹活
System.out.println("主執行緒正在幹活");
//阻塞當前執行緒,等待返回結果.
System.out.println("等待返回結果:" + futureTask.get());
System.out.println("主執行緒所有的活都幹完了");
}
}
//呼叫輸出
子執行緒正在幹活
主執行緒正在幹活
等待返回結果:實現Callable介面,重寫Call方法
主執行緒所有的活都幹完了
複製程式碼
說到這裡,可能有些人和我一樣迷糊,ExecutorService可以通過submit和execute進行執行任務.那麼這兩者區別是啥.我們來簡單的總結一下.
對應原始碼
//submit方法
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
//execute方法
void execute(Runnable command);
複製程式碼
從原始碼對比可以知道.
- 接收的引數不一樣.execute僅能接收Runnable型別.
- submit()有返回值,而execute()沒有
- submit()可以進行Exception處理.
通過總結會發現這其實就與前面介紹的實現Runable與Callable介面的特點類似.
三. ThreadPoolExecutor介紹
所有執行緒池都是通過ThreadPoolExecutor來建立.
對應原始碼
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
複製程式碼
引數解析
- corePoolSize : 核心執行緒數
- maximumPoolSize : 最大執行緒數
- keepAliveTime : 非核心執行緒閒置時的超時時間.如果設定了allowCoreThreadTimeOut為true,那麼也將作用於核心執行緒.
- unit : 時間單位
- workQueue : 任務佇列.存放Runnable任務.
另外還有2個引數,一般我們不需要去手動設定.
- ThreadFactory : 執行緒工廠,建立執行緒用.
- RejectedExecutionHandler : 任務佇列已滿或者無法成功執行任務時,會呼叫此handler的rejectedExecution方法丟擲異常通知呼叫者,俗稱飽和策略.
理解ThreadPoolExecutor處理的流程
- 在提交任務時,檢查是否達到核心執行緒數量.
- 未達到,則啟動核心執行緒去執行任務.
- 已達到,進行下一步.
- 檢查任務佇列是否已滿.
- 佇列未滿,加入任務佇列.
- 佇列已滿,進行下一步.
- 檢查是否到達最大執行緒數.
- 未達到,啟動非核心執行緒去執行任務.
- 已達到,執行飽和策略.
- 一旦有執行緒處於閒置時,就會去佇列中獲取任務執行.
四. Executors工廠類
通過Executors提供四種執行緒池,newFixedThreadPool、newSingleThreadExecutor、newScheduledThreadPool、ewCachedThreadPool.
- FixedThreadPool
可重用固定執行緒數的執行緒池.
對應原始碼
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
複製程式碼
- 只有核心執行緒.數量固定
- 無超時機制,佇列大小無限制.
使用示例
private static void executeFixedThreadPool() {
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 20; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println("執行緒:" + Thread.currentThread().getName());
}
});
}
}
複製程式碼
- SingleThreadExecutor
單執行緒的執行緒池
對應原始碼
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
複製程式碼
同FixedThreadPool類似.
- 固定只有一個核心執行緒. 因此它能確保所有任務在一個執行緒中按順序執行.
使用示例
private static void executeSingleThreadExecutor() {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 20; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println("執行緒:" + Thread.currentThread().getName());
}
});
}
}
複製程式碼
- ScheduledThreadPool
支援定時和週期性任務的執行緒池
對應原始碼
ScheduledExecutorService.java
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
ScheduledThreadPoolExecutor.java
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
複製程式碼
同FixedThreadPool類似.
- 核心執行緒數量固定,非核心執行緒不定.
使用示例
private static void executeScheduledThreadPool(int flag) {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("後"+System.currentTimeMillis());
// try {
// Thread.sleep(10000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println("執行緒:" + Thread.currentThread().getName());
}
};
System.out.println("前"+System.currentTimeMillis());
switch (flag) {
case 0:
//延遲1000毫秒後開始執行
executorService.schedule(runnable, 1000, TimeUnit.MILLISECONDS);
break;
case 1:
//延遲1000毫秒後開始執行,後面每隔2000毫秒執行一次.強調任務的執行頻率,不受任務執行時間影響,過時不候.
executorService.scheduleAtFixedRate(runnable, 1000, 2000, TimeUnit.MILLISECONDS);
break;
case 2:
//延遲1000毫秒後開始執行,後面每次延遲3000毫秒執行一次.強調任務執行的間隔.
executorService.scheduleWithFixedDelay(runnable, 1000, 3000, TimeUnit.MILLISECONDS);
break;
default:
break;
}
}
複製程式碼
- CachedThreadPool
對應原始碼
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
複製程式碼
- 只有非核心執行緒.
- 有超時機制,佇列無法儲存,被立即執行.
因此適合大量的耗時較少的任務.
使用示例
private static void executeCachedThreadPool() {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 20; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println("執行緒:" + Thread.currentThread().getName());
}
});
}
}
複製程式碼
關於執行緒池相關就介紹到這裡了.
由於本人技術有限,如有錯誤的地方,麻煩大家給我提出來,本人不勝感激,大家一起學習進步.
參考連結: