ThreadPoolExecutor 的三種提交任務方式
學習內容:
ExecutorService線程池的應用...
1.如何創建線程池...
2.調用線程池的方法,獲取線程執行完畢後的結果...
3.關閉線程...
首先我們先了解一下到底什麽是線程池,只有了解了其中的道理,我們才能夠進行應用...java.util.concurrent.ExecutorService表述了異步執行的機制
首先我們簡單的舉一個例子...
package executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Executor {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("cc");
ExecutorService executorService=Executors.newFixedThreadPool(10);
executorService.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
System.out.println("aa");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
System.out.println("bb");
}
}
這裏我們指定了十個線程處於一個線程池內部,線程池的原理其實就是對多線程的一個管理,為了實現異步機制的一種方法,其實就是多個線程執行多個任務,最終這些線程通過線程池進行管理...不用手動去維護...一次可以處理多個任務,這樣就可以迅速的進行相應...比如說一個網站成為了熱點網站,那麽對於大量的點擊量,就必須要對每一次的點擊做出迅速的處理,這樣才能達到更好的交互效果...這樣就需要多個線程去處理這些請求,以便能夠更好的提供服務...
1. 簡單的說一下如何創建線程池進行初始化....創建線程有幾種常用方式...這裏都是使用了Executors工廠來實例化對象,同時我們也可以根據需求自己去寫一個ExecutorService...這幾種常用的方法有一定的區別...
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
ExecutorService executorService2 = Executors.newFixedThreadPool(10);
ExecutorService executorService3 = Executors.newScheduledThreadPool(10);
ExecutorService executorService4 = Executors.newCacheThreadPool();
Executors.newSingleThreadExecutor()
|
單例線程,表示在任意的時間段內,線程池中只有一個線程在工作... |
Executors.newCacheThreadPool()
|
緩存線程池,先查看線程池中是否有當前執行線程的緩存,如果有就resue(復用),如果沒有,那麽需要創建一個線程來完成當前的調用.並且這類線程池只能完成一些生存期很短的一些任務.並且這類線程池內部規定能resue(復用)的線程,空閑的時間不能超過60s,一旦超過了60s,就會被移出線程池. |
Executors.newFixedThreadPool(10) | 固定型線程池,和newCacheThreadPool()差不多,也能夠實現resue(復用),但是這個池子規定了線程的最大數量,也就是說當池子有空閑時,那麽新的任務將會在空閑線程中被執行,一旦線程池內的線程都在進行工作,那麽新的任務就必須等待線程池有空閑的時候才能夠進入線程池,其他的任務繼續排隊等待.這類池子沒有規定其空閑的時間到底有多長.這一類的池子更適用於服務器. |
Executors.newScheduledThreadPool(10) |
調度型線程池,調度型線程池會根據Scheduled(任務列表)進行延遲執行,或者是進行周期性的執行.適用於一些周期性的工作. |
這就是線程池創建的幾種方式...我們需要根據不同的需求來適當的選擇到底使用哪種線程池...
2.那麽創建了線程池以後就需要對線程池進行調用..將任務加載到其中...
i.ExecutorService.execute(Runnable);
第一種調用方式...通過這種方式將線程任務加載到線程池當中...我們可以添加多個任務...貼上一個完整的代碼...大家看一下代碼的解釋就明白到底是怎麽回事了..不難理解...
package executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Executor {
/**
* @param args
*
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ExecutorService executorService=Executors.newFixedThreadPool(2);//定義了線程池中最大存在的線程數目...
//添加了第一個任務...這個任務會一直被執行...
executorService.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
System.out.println("aa");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
//添加第二個任務,被執行三次停止...
executorService.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
int i=0;
while(true){
i++;
System.out.println("bb");
if(i==3){
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
/*
* @param
* 第三個任務...只有當第二個任務被執行三次之後才能被執行...
* 由於三次前,線程池已經滿了,這個任務是輪不到被執行的..只能排隊進行等待.
* 三次之後,第二個任務被終止,也就是線程池中出現了空閑的狀態,所以這個任務將被放入到線程池中執行...
* */
executorService.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
System.out.println("cc");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
}
}
ii.executorService.submit(Runnable) 第二種調用方式...這種方式與第一種的區別在於可以使用一個Future對象來判斷當前的線程是否執行完畢...但是這種方法只能判斷當前的線程是否執行完畢,無法返回數據信息...
Future future = executorService.submit(new Runnable() {
public void run() {
System.out.println("Asynchronous task");
}
});
//如果任務結束執行則返回 null
System.out.println("future.get()=" + future.get());
iii.executorService.submit(Callable)... 第三種調用方式...這種調用方式與前一種有所不同,傳遞的參數為Callable對象,Callable與Runnbale很相似,但是Callable的call()方法可以返回數據信息...通過Future就能夠獲取到其中的信息..而Runnbale.run()方法時無法獲取數據信息的....Future應用於多線程...可以獲取call()方法返回的數據信息...其實他是一種模式,是為了性能優化而提供的一種思想...這裏我就不說Future...
uture future = executorService.submit(new Callable(){
public Object call() throws Exception {
System.out.println("Asynchronous Callable");
return "Callable Result";
}
});
System.out.println("future.get() = " + future.get());
//上述樣例代碼會輸出如下結果:
//Asynchronous Callable
//future.get() = Callable Result
iv.inVokeAny()...第四種調用方式...方法 invokeAny() 接收一個包含 Callable 對象的集合作為參數。調用該方法不會返回 Future 對象,而是返回集合中某一個 Callable 對象的結果,而且無法保證調用之後返回的結果是哪一個Callable,只知道它是這些 Callable 中一個執行結束的 Callable 對象...說實話這個方法我不知道它創建的目的到底是什麽...這裏執行後的結果是隨機的...也就是輸出是不固定的....
ExecutorService executorService = Executors.newSingleThreadExecutor();
Set<Callable<String>> callables = new HashSet<Callable<String>>();
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 1";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 2";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 3";
}
});
String result = executorService.invokeAny(callables);
System.out.println("result = " + result);
v.inVokeAll()這個方法和上面不同的地方就在於它可以返回所有Callable的執行結果...獲取到所有的執行結果,我們可以對其進行管理...相對而言,我覺得這個方法比上一個更實用吧...
ExecutorService executorService = Executors.newSingleThreadExecutor();
Set<Callable<String>> callables = new HashSet<Callable<String>>();
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 1";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 2";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 3";
}
});
List<Future<String>> futures = executorService.invokeAll(callables);
for(Future<String> future : futures){
System.out.println("future.get = " + future.get());
3.線程池的關閉...
當我們不需要使用線程池的時候,我們需要對其進行關閉...有兩種方法可以關閉掉線程池...
i.shutdown()...
shutdown並不是直接關閉線程池,而是不再接受新的任務...如果線程池內有任務,那麽把這些任務執行完畢後,關閉線程池....
ii.shutdownNow()
這個方法表示不再接受新的任務,並把任務隊列中的任務直接移出掉,如果有正在執行的,嘗試進行停止...
大家自己試著運行下面的代碼就了解其中到底是怎麽回事了...
package executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Executor {
/**
* @param args
*
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ExecutorService executorService=Executors.newFixedThreadPool(1);//定義了線程池中最大存在的線程數目...
//添加了第一個任務...這個執行三次停止...
executorService.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
int j=0;
while(true){
j++;
System.out.println("aa");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(j==3){
break;
}
}
}
});
//添加第二個任務,由於使用executorService.shutdown(),由於它的加入是在這個方法調用之前的,因此這個任務也會被執行...
//如果我們使用了executorService.shutdownNow();方法,就算是他在之前加入的,由於調用了executorService.shutdownNow()方法
//那麽這個任務將直接被移出隊列並且不會被執行...
executorService.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
int i=0;
while(true){
i++;
System.out.println("bb");
if(i==3){
break;
}
}
}
});
executorService.shutdown();//這裏無論使用了那種方法,都會拋出一個異常...
/*
* @param
* 第三個任務...只有當第二個任務被執行三次之後才能被執行...
* 由於三次前,線程池已經滿了,這個任務是輪不到被執行的..只能排隊進行等待.
* 三次之後,第二個任務被終止,也就是線程池中出現了空閑的狀態,所以這個任務將被放入到線程池中執行...
* */
executorService.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
System.out.println("cc");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
}
}
ThreadPoolExecutor 的三種提交任務方式