@author:小馬快跑
@email:[email protected]
@github:https://github.com/crazyqiang
學習線程池必備知識:
在開始介紹線程池之前,先來介紹下Callable和Future的概念,衆所周知,Android中實現多線程的方式有兩種,實現Runnable接口或者繼承一個Thread,但是這兩種方式都有一個缺點:在任務執行完成之後沒有返回結果,所以在Java 1.5之後,出現了Callable和Future,通過他們構建的線程,可以在線程執行完成之後得到返回結果。
先來對比Runnable 和Callable:
Runnable :
public interface Runnable {
public abstract void run();
}
Callable:
public interface Callable<V> {
V call() throws Exception;
}
Runnable 接口中的run()方法的返回值是void,所以Runnable無返回值;而Callable接口中的call()方法的返回值是V,也可以拋出異常,所以Callable是可以有返回值的。接着來看下Future:
Future:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Future用來獲取異步計算的結果,即是獲取Callable任務的結果
Future | 備註 |
---|---|
cancel(boolean) | 嘗試取消異步任務的執行。如果任務已經執行完成、已經被取消、因爲某種原因不能被取消,則返回false;如果任務正在執行,並且mayInterruptIfRunning爲true,那麼會調用interrupt()嘗試打斷任務。該方法返回結果後,isDone()總會返回true |
isCancelled() | 如果在任務完成前被取消,返回true |
isDone() | 如果任務完成則返回true。任務完成包括正常結束、任務被取消、任務發生異常,都返回true |
get() | 獲取異步任務執行結果,如果沒有返回,則阻塞等待 |
get(long timeout, TimeUnit unit) | 在給定的時間內等待獲取異步任務結果,如果超時還未獲取到結果,則會拋出TimeoutException |
Future只是一個接口,還需要看Future的具體實現類:
以FutureTask爲例,具體分析下FutureTask:
public class FutureTask<V> implements RunnableFuture<V> {
................其他.....................
}
咦?FutureTask並沒有實現Future接口,而是實現了一個叫RunnableFuture的接口,這貨從哪裏蹦出來的?我們點進去看一下:
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
吆西!原來這貨不僅繼承了Future接口,還繼承了Runnable接口(PS:接口可以多繼承),那麼我們的FutureTask也就實現了Runnable和Future接口
FutureTask | 備註 |
---|---|
boolean isCancelled() | 同Future |
boolean isDone() | 同Future |
boolean cancel(boolean mayInterruptIfRunning) | 同Future |
V get() | 同Future |
V get(long timeout, TimeUnit unit) | 同Future |
void run() | 如果這個任務沒有被取消,將直接在當前線程內執行任務 |
FutureTask有兩個構造方法:
public FutureTask(Callable<V> callable)
public FutureTask(Runnable runnable, V result)
我們看到FutureTask初始化時不僅可以傳入Callable,還可以傳入一個Runnable和一個”返回結果result”,這裏爲什麼返回結果要加引號呢,來看下源碼就知道了:
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
上面FutureTask()的初始化構造參數中調用了Executors.callable():
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
private static final class RunnableAdapter<T> implements Callable<T> {
private final Runnable task;
private final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
//把傳入的值直接返回
return result;
}
}
通過源碼我們看到,在最後的call()方法中,直接把傳入的值返回了,所以FutureTask(Runnable runnable, V result)得到的只是預設的結果,Runnable並不會得到執行的結果。如果不需要預設的返回結果,可以像下面這樣初始化:
Future<?> f = new FutureTask<Void>(runnable, null)
FutureTask的使用:
//初始化一個線程池
ExecutorService executor = = Executors.newSingleThreadExecutor();
//new 一個Callable並傳入FutureTask
FutureTask<String> future =new FutureTask<>(new Callable<String>() {
public String call() {
//do something
return result;
}});
executor.execute(future);
//在異步任務執行期間可以做一些其他的事情
displayOtherThings();
//通過future.get()得到異步任務執行結果
String result=future.get();
ExecutorService :
ExecutorService繼承自Executor接口,並提供了管理線程以及創建可以追蹤一個或多個異步任務的進展的Future的方法。
public interface ExecutorService extends Executor {
//無法提交新任務,但是已經提交的任務將繼續執行,當執行完成後關閉線程池
void shutdown();
//嘗試停止所有正在執行的任務,暫停等待任務的處理,並返回等待執行的任務列表。
List<Runnable> shutdownNow();
//如果線程池已經關閉則返回true
boolean isShutdown();
//如果所有任務都在線程池關閉後完成,則返回true。注意,除非首先調用 shutdown 或 shutdownNow,否則 isTerminated 永不爲 true。
boolean isTerminated();
//阻塞等待,直到所有任務在關閉請求後完成執行,或者超時發生,或者當前線程被中斷
// 如果此執行程序終止,則返回 true;如果終止前超時了,則返回 false
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
//提交一個 Runnable 任務用於執行,並返回一個表示該任務的 Future。該 Future 的 get 方法在成功 完成時將會返回 null
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
//執行給定的任務,當所有任務完成或超時期滿時(無論哪個首先發生),返回保持任務狀態和結果的 Future 列表。
//返回列表的所有元素的 Future.isDone() 爲 true。一旦返回後,即取消尚未完成的任務。
//注意,可以正常地或通過拋出異常來終止已完成 任務。如果此操作正在進行時修改了給定的
// collection,則此方法的結果是不確定的。
<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;
//執行給定的任務,如果在給定的超時期滿前某個任務已成功完成(也就是未拋出異常),則返回其結果。
//一旦正常或異常返回後,則取消尚未完成的任務。如果此操作正在進行時修改了給定的 collection,
//則此方法的結果是不確定的。
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
線程池的使用:
使用線程池的好處:
1、當執行大量異步任務時,線程池能提供更好的性能體驗,因爲線程池能減少每個任務的調用開銷,重用存在的線程,減少對象創建、消亡的開銷
2、還可以提供綁定和管理資源 (包括執行任務時使用的線程) 的方法。有效控制最大併發線程數,提高系統資源的使用率,同時避免過多資源競爭,避免堵塞
3、 提供定時執行、定期執行、單線程、併發數控制等功能。
Android中提供了四種線程池,四種線程池內部實現都是直接或者間接用的ThreadPoolExecutor。
1、Executors.newCachedThreadPool():只有Integer.MAX_VALUE個非核心線程,當有任務來時,如果線程池中的線程都處於活動狀態,那麼會新建線程來執行,否則就會利用空閒線程去執行,空閒線程都會有一個超時機制,超過60秒的空閒線程會被回收。任務隊列爲空集合,所以所有任務都會被立即執行,CachedThreadPool適合執行大量的耗時較少的操作。
效果圖:
因爲非核心線程數有無限個,所以不管有多少任務都可以並行執行,可以看到上述5個任務一起執行,核心代碼:
//初始化線程池
ExecutorService threadPool= Executors.newCachedThreadPool();
//初始化Callable
Callable<Integer> callable = new Callable<Integer>() {
int num = 0;
@Override
public Integer call() throws Exception {
while (num < 100) {
num++;
sendMsg(num, what);
Thread.sleep(50);
}
return 100;
}
};
//執行線程任務
threadPool.submit(callable);
2、Executors.newFixedThreadPool(int nThreads):只有核心線程並且不會被回收,任務隊列沒有大小限制。
效果圖:
因爲核心線程數參數我們傳入的是4,可所以看到先執行其中的4個任務,等待有任務執行完成後接着去執行第5個任務,核心代碼:
//初始化線程池
ExecutorService threadPool= Executors.newFixedThreadPool(4);
//初始化Callable
Callable<Integer> callable = new Callable<Integer>() {
int num = 0;
@Override
public Integer call() throws Exception {
while (num < 100) {
num++;
sendMsg(num, what);
Thread.sleep(50);
}
return 100;
}
};
//執行線程任務
threadPool.submit(callable);
3、Executors.newSingleThreadExecutor():內部只有一個核心線程,所有任務按順序執行 統一所有任務到一個線程中,使得這些任務不用處理線程同步問題。
效果圖:
可以看到每次只能執行一個任務,也就是所有任務都是串行執行的,核心代碼:
//初始化線程池
ExecutorService threadPool= Executors.newSingleThreadExecutor();
//初始化Callable
Callable<Integer> callable = new Callable<Integer>() {
int num = 0;
@Override
public Integer call() throws Exception {
while (num < 100) {
num++;
sendMsg(num, what);
Thread.sleep(50);
}
return 100;
}
};
//執行線程任務
threadPool.submit(callable);
4、Executors.newScheduledThreadPool(int corePoolSize): 核心線程是固定的,非核心線程是不固定的,非核心線程閒置時會被立即回收,主要用於執行定時任務和具有週期性的重複任務。
效果圖:
ScheduledExecutorService傳入的核心線程數是4,並且是在延遲2秒之後執行的,核心代碼:
//初始化線程池,核心線程數爲4
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(4);
//初始化Callable
Callable<Integer> callable = new Callable<Integer>() {
int num = 0;
@Override
public Integer call() throws Exception {
while (num < 100) {
num++;
sendMsg(num, what);
Thread.sleep(50);
}
return 100;
}
};
//延遲2秒後執行
scheduledPool.schedule(callable, 2, TimeUnit.SECONDS);
除上述延遲執行的方法外,ScheduledExecutorService中還有下列方法:
public interface ScheduledExecutorService extends ExecutorService {
//延遲delay(TimeUnit 爲單位)之後執行Runnable
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
//延遲delay(TimeUnit 爲單位)之後執行Callable
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
//延遲initialDelay之後每period時間執行一次Callable,循環執行
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
//第一次延遲initialDelay之後執行,之後在每次完成後延遲delay時間執行下一次操作
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
}
本文例子中完整代碼已上傳github:Android線程池的使用