學習內容:
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(); } } } }); } }