Java多執行緒(ExecutorService 、Executors、Callable、Future、FutureTask)
前言:我們一般通過繼承Thread類重寫run方法或者實現runnable介面重寫run方法,最後建立和啟動一個執行緒,但是都需要自己建立、啟動Thread物件。執行緒池可以實現幫助我們管理Thread物件,至於要使用幾個執行緒,什麼時候啟動這些執行緒,是開啟多個執行緒還是用單個執行緒來完成這些任務,我們無需操心。
Java通過Executors提供四種執行緒池
- newCachedThreadPool:建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。(執行緒最大併發數不可控制)
- newFixedThreadPool:建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。
- newScheduledThreadPool:建立一個定長執行緒池,支援定時及週期性任務執行。
- newSingleThreadExecutor:建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。
package threadTest.executors; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @ClassName CachedThreadPool * @Description TODO * @Author Kikityer * @Date 2018/11/21 19:04 * @Version 1.0.0 **/ public class CachedThreadPool { public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); for (int i = 0;i < 5; i++){ exec.submit(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+" is doing task"); } }); } exec.shutdown(); } } //結果: /** *pool-1-thread-1 is doing task *pool-1-thread-2 is doing task *pool-1-thread-3 is doing task *pool-1-thread-4 is doing task *pool-1-thread-5 is doing task */
通過for迴圈建立了5個任務,通過submit方法開啟了五個執行緒處理這五個任務。
submit有兩個常用的方法:
Future<?> submit(Runnable task) 接收Runnable型別入參,而Runnable無返回值 。
<T> Future<T> submit(Callable<T> task) 接收Callable型別入參 ,Callable入參允許任務返回值。
package threadTest.executors;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
/**
* @ClassName CallableAndFuture
* @Description TODO
* @Author Kikityer
* @Date 2018/11/21 19:20
* @Version 1.0.0
**/
public class CallableAndFuture {
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
List<Callable<String>> taskList = new ArrayList<>(); //任務列表,用於存放任務
for (int i = 0; i < 5; i++){
taskList.add(new MyCallable()); //建立5個任務,並且新增到列表中
}
List<Future<String>> resultList = new ArrayList<>(); //結果列表,用於存放任務執行的結果
try {
resultList = exec.invokeAll(taskList); //invokeAll是等所有任務完成後返回代表結果的Future列表
exec.shutdown();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (Future<String> future : resultList){ //遍歷結果列表,打印出每個任務的結果,即打印出每個當前執行緒的名字(String型別)
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
/**
* MyCallable實現Callable介面,建立一個可以有返回值的任務類
*/
class MyCallable implements Callable<String>{
@Override
public String call() {
return Thread.currentThread().getName();
}
}
//結果:
/**
*pool-1-thread-1
*pool-1-thread-2
*pool-1-thread-3
*pool-1-thread-4
*pool-1-thread-5
*/
在上面的程式碼中,我們建立了一個類實現了一個Callable介面,此類是一個有返回值的任務類。而後開啟了一個執行緒池用來管理執行緒。從結果可以看出我們得到了任務類所返回的資訊。其中用到了Future介面、invokeAll方法、get方法。
Future介面
Future 表示非同步計算的結果。它提供了檢查計算是否完成的方法,以等待計算的完成,並獲取計算的結果。上面的例子中exec執行器執行了一個Callable型別的任務列表然後得到了Futuer型別的結果列表resultList。
invokeAll方法
invokeAll批量執行所有任務。和submit的區別是:submit提交單個任務
get方法
等待計算完成,獲取其結果。get方法會阻塞直到結果返回。
FutureTask
FutureTask類是 Future 介面的一個實現。FutureTask類實現了RunnableFuture介面,RunnableFuture繼承了Runnable介面和Future介面所以:
可以看出FutureTask可以當作一個有返回值的Runnable任務來用。
- FutureTask可以作為Runnable被執行緒執行
- 可以作為Future得到傳入的Callable物件的返回值 如:futureTask.get()