1.線程池
1.1什么情況下使用線程池
- 單個任務處理的時間比較短.
- 將需處理的任務的數量大.
1.2使用線程池的好處
- 減少在創建和銷毀線程上所花的時間以及系統資源的開銷.
- 如果不使用線程池,有可能造成系統創建大量線程而導致消耗系統內存以及”過度切換”;
2.ExecutorService和Executors
2.1簡介
ExecutorService是一個接口,繼承了Executor,
1
2
|
public interface ExecutorService extend Executor{ } |
Executor也是一個接口,該接口只包含一個方法:
1
2
3
|
public interface Executor { void execute(Runnable command); } |
Java里面的線程池的頂級接口是Excutor,但是嚴格意義上來說>>Exector并不是一個線程池,而只是一個執行線程的工具,真正的線程>池接口是ExecutorService.
3.Executors
它是一個靜態工廠類,它能生產不同類型的線程池,部分源碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class Executors { //newFixedThreadPool public static ExecutorService newFixedThreadPool( int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } //newCacheThreadPool public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor( 0 , Integer.MAX_VALUE,60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } //newScheduledThreadPool public static ScheduledExecutorService newScheduledThreadPool( int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } //newStringooo } |
先看一個具體的例子,用例子來說明它們之間的異同.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
package thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; /** * Created by yang on 16-7-11. */ public class Ch09_Executor { private static void run(ExecutorService threadPool) { for ( int i = 1 ; i < 5 ; i++) { final int taskID=i; threadPool.execute( new Runnable() { @Override public void run() { for ( int i= 1 ;i< 5 ;i++){ try { Thread.sleep( 20 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println( "第" +taskID+ "次任務的第" +i+ "次執行" ); } } }); } threadPool.shutdown(); } public static void main(String[] args) { //創建可以容納3個線程的線程池 ExecutorService fixedThreadPool= Executors.newFixedThreadPool( 3 ); //線程池的大小會根據執行的任務動態的分配 ExecutorService cacheThreadPool=Executors.newCachedThreadPool(); //創建單個線程的線程池,如果當前線程在執行任務時突然中斷,則會創建一個新的線程替換它繼續執行. ExecutorService singleThreadPool=Executors.newSingleThreadExecutor(); //效果類似于Timer定時器 ScheduledExecutorService scheduledThreadPool=Executors.newScheduledThreadPool( 3 ); // run(fixedThreadPool); //(1) //run(cacheThreadPool); //(2) // run(singleThreadPool); //(3) // run(scheduledThreadPool); //(4) } } |
4. 4種常用的線程池
4.1 CachedThreadPool
CachedThreadPool會創建一個緩存區,將初始化的線程緩存起來,會終止并且從緩存中移除已有6秒未被使用的線程.
如果線程有可用,就使用之前創建好的線程.如果線程沒有可用的,就新創建線程.
.重用:
緩存型池子,先看看池中有沒有以前建立的線程,如果有,就reuse,如果沒有,就新建一個新的線程加入池中,
使用場景:
緩存型池子通常用于執行一些生存期很短的異步型任務,因此在一些面向連接的Daemon型SERVER中用地不多.
超時:
能reuse的線程,必須是timeout IDLE內的池中線程,缺省timeout是60s,超過這個IDLE時長,線程實例將被終止及移除池.
結束:
放入CachedThreadPool的線程不必擔心其結束,超過TIMEOUT不活動,其會被自動終止.
實例解說:
去掉(2)的注釋,運行,得到的運行結果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
第 1 次任務的第 1 次執行 第 3 次任務的第 1 次執行 第 2 次任務的第 1 次執行 第 4 次任務的第 1 次執行 第 3 次任務的第 2 次執行 第 1 次任務的第 2 次執行 第 2 次任務的第 2 次執行 第 4 次任務的第 2 次執行 第 3 次任務的第 3 次執行 第 1 次任務的第 3 次執行 第 2 次任務的第 3 次執行 第 4 次任務的第 3 次執行 第 3 次任務的第 4 次執行 第 2 次任務的第 4 次執行 第 4 次任務的第 4 次執行 第 1 次任務的第 4 次執行 |
從結果可以看出,4個任務是交替執行的.
4.2FixedThreadPool
在FixedThreadPool中,有一個固定大小的池,
如果當前需要執行的任務超過池大小,那么多出去的任務處于等待狀態,直到有空閑下來的線程執行任務。
如果當前需要執行的任務小于池大小,空閑線程不會被銷毀.
重用:
fixedThreadPool與cacheThreadPool差不多,也是能reuse就用,但不能隨時建新的線程
固定數目
其獨特之處在于,任意時間點,最多只能有固定數目的活動線程存在,此時如果有新的線程要建立,只能放在另外的隊列中等待,直到當前的線程中某個線程終止直接被移出池子
超時:
和cacheThreadPool不同,FixedThreadPool沒有IDLE機制
使用場景:
所以FixedThreadPool多數針對一些很穩定很固定的正規并發線程,多用于服務器
源碼分析:
從方法的源代碼看,cache池和fixed 池調用的是同一個底層池,只不過參數不同.
fixed池線程數固定,并且是0秒IDLE(無IDLE)
cache池線程數支持0-Integer.MAX_VALUE(顯然完全沒考慮主機的資源承受能力),60秒IDLE
實例解說:
去掉(1)的注釋,運行結果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
第 1 次任務的第 1 次執行 第 3 次任務的第 1 次執行 第 2 次任務的第 1 次執行 第 1 次任務的第 2 次執行 第 3 次任務的第 2 次執行 第 2 次任務的第 2 次執行 第 1 次任務的第 3 次執行 第 3 次任務的第 3 次執行 第 2 次任務的第 3 次執行 第 1 次任務的第 4 次執行 第 3 次任務的第 4 次執行 第 2 次任務的第 4 次執行 第 4 次任務的第 1 次執行 第 4 次任務的第 2 次執行 第 4 次任務的第 3 次執行 第 4 次任務的第 4 次執行 |
創建了一個固定大小的線程池,容量是為3,然后循環執行4個任務,由輸出結果可以看出,前3個任務首先執行完,然后空閑下來的線程去執行第4個任務.
4.3SingleThreadExecutor
- SingleThreadExector得到的是一個單個線程,這個線程會保證你的任務執行完成.
- 單例線程,任意時間池中只能有一個線程
- 如果當前線程意外終止,會創建一個新的線程繼續執行任務,這和我們直接創建線程不同,也和newFixedThreadPool(1)不同.
- 用的是和cache池和fixed池相同的底層池,但線程數目是1-1,0秒IDLE(無IDLE)
去掉(3)注釋. 看執行結果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
第 1 次任務的第 1 次執行 第 1 次任務的第 2 次執行 第 1 次任務的第 3 次執行 第 1 次任務的第 4 次執行 第 2 次任務的第 1 次執行 第 2 次任務的第 2 次執行 第 2 次任務的第 3 次執行 第 2 次任務的第 4 次執行 第 3 次任務的第 1 次執行 第 3 次任務的第 2 次執行 第 3 次任務的第 3 次執行 第 3 次任務的第 4 次執行 第 4 次任務的第 1 次執行 第 4 次任務的第 2 次執行 第 4 次任務的第 3 次執行 第 4 次任務的第 4 次執行 |
四個任務是順序執行的.
4.4 ScheduledThreadPool
ScheduledThreadPool是一個固定大小的線程池,與FixedThreadPool類似,執行的任務是定時任務.
去掉(4)的注釋得到的結果和FixedThreadPool得到的結果相同,ScheduledThreadPool的主要沒有在這里,而是定時任務,看下面這個例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
package thread; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * Created by yang on 16-7-11. */ public class MyScheduledTask implements Runnable { private String tname; public MyScheduledTask(String name){ this .tname=name; } public void run(){ System.out.println(tname+ "任務時延時2秒執行!" ); } public static void main(String[] args) { ScheduledExecutorService scheduledPool= Executors.newScheduledThreadPool( 2 ); ScheduledExecutorService singSchedulePool=Executors.newSingleThreadScheduledExecutor(); MyScheduledTask mt1= new MyScheduledTask( "mt1" ); MyScheduledTask mt2= new MyScheduledTask( "mt2" ); //以scheduledThreadPool啟動mt1任務執行 scheduledPool.schedule(mt1, 2 , TimeUnit.SECONDS); //用singlescheduledthreadPool啟動mt2; singSchedulePool.schedule(mt2, 2000 ,TimeUnit.MILLISECONDS); scheduledPool.shutdown(); singSchedulePool.shutdown(); } } |
結果:
1
2
|
mt1任務時延時 2 秒執行! mt2任務時延時 2 秒執行! |
在程序運行2秒后,才會有結果顯示,說明線程在2秒后執行的.
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
原文鏈接:http://blog.csdn.net/whoamiyang/article/details/51883353