- 論壇徽章:
- 0
|
java并發(fā)編程--線程池初步
[coolxing按: 轉(zhuǎn)載請(qǐng)注明作者和出處, 如有謬誤, 歡迎在評(píng)論中指正.]
服務(wù)器應(yīng)用程序經(jīng)常需要處理執(zhí)行時(shí)間很短而數(shù)目巨大的請(qǐng)求, 如果為每一個(gè)請(qǐng)求創(chuàng)建一個(gè)新的線程, 會(huì)導(dǎo)致一些問(wèn)題的出現(xiàn), 如:
1. 性能瓶頸. 線程的創(chuàng)建和銷毀需要執(zhí)行大量的后臺(tái)操作, 如果單個(gè)請(qǐng)求的執(zhí)行時(shí)間很短, 有可能花在創(chuàng)建和銷毀線程上的時(shí)間大于真正執(zhí)行請(qǐng)求的時(shí)間.
2. 可能會(huì)導(dǎo)致資源不足. 大量的并發(fā)請(qǐng)求意味著需要?jiǎng)?chuàng)建大量的線程, 過(guò)多的線程存在會(huì)吞噬大量的系統(tǒng)資源, 而且CPU需要在這些線程間不斷切換, 這可能引發(fā)"切換過(guò)度"的問(wèn)題.
為了適應(yīng)上述場(chǎng)合, java在JDK1.5中引入了線程池的概念. 線程池中存放著一定數(shù)量的已創(chuàng)建好的線程, 當(dāng)一個(gè)請(qǐng)求到來(lái)時(shí), 只需從線程池中取出一個(gè)線程來(lái)執(zhí)行請(qǐng)求, 請(qǐng)求完成后再將線程歸還給線程池. 同時(shí), 我們可以為線程池指定最大的線程數(shù)量, 當(dāng)池中所有線程都處于活動(dòng)狀態(tài)下, 新的任務(wù)會(huì)排隊(duì)等候, 直到之前的某個(gè)任務(wù)處理完成后, 新的任務(wù)才能得到處理.
創(chuàng)建線程池. java.util.concurrent.Executors類提供了多個(gè)靜態(tài)方法用于創(chuàng)建線程池.
|--public static ExecutorService newFixedThreadPool(int nThreads): 創(chuàng)建一個(gè)可重用的固定線程數(shù)的線程池. 如果池中所有的nThreads個(gè)線程都處于活動(dòng)狀態(tài)時(shí)提交任務(wù)(任務(wù)通常是Runnable或Callable對(duì)象), 任務(wù)將在隊(duì)列中等待, 直到池中出現(xiàn)可用線程.
|--public static ExecutorService newCachedThreadPool(): 調(diào)用此方法創(chuàng)建的線程池可根據(jù)需要自動(dòng)調(diào)整池中線程的數(shù)量. 執(zhí)行任務(wù)時(shí)將重用存在先前創(chuàng)建的線程(如果池中存在可用線程的話). 如果池中沒(méi)有可用線程, 將創(chuàng)建一個(gè)新的線程, 并將其添加到池中. 池中的線程超過(guò)60秒未被使用就會(huì)被銷毀, 因此長(zhǎng)時(shí)間保持空閑的CachedThreadPool不會(huì)消耗額外的資源.
|--public static ExecutorService newSingleThreadExecutor(): 創(chuàng)建一個(gè)單線程的Executor. 這個(gè)Executor保證按照任務(wù)提交的順序依次執(zhí)行任務(wù).
|--public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize): 創(chuàng)建一個(gè)可重用的固定線程數(shù)的線程池. ScheduledExecutorService是ExecutorService的子接口, 調(diào)用ScheduledExecutorService的相關(guān)方法, 可以延遲或定期執(zhí)行任務(wù).
以上靜態(tài)方法均使用默認(rèn)的ThreadFactory(即Executors.defaultThreadFactory()方法的返回值)創(chuàng)建線程, 如果想要指定ThreadFactory, 可調(diào)用他們的重載方法.通過(guò)指定ThreadFactory, 可以定制新建線程的名稱, 線程組, 優(yōu)先級(jí), 守護(hù)線程狀態(tài)等.
如果Executors提供的創(chuàng)建線程池的方法無(wú)法滿足要求, 可以使用ThreadPoolExecutor類創(chuàng)建線程池.
提交任務(wù). 所有的線程池都是ExecutorService及其子類的對(duì)象, 因此, 可以調(diào)用ExecutorService的相關(guān)方法提交任務(wù).
|--void execute(Runnable command): 使用池中已存在的線程或新建一個(gè)線程執(zhí)行command.
Java代碼程序的輸出為:
- ----------------FixedThreadPool---------------------
- pool-1-thread-1: is running!
- pool-1-thread-2: is running!
- pool-1-thread-2: is running!
- pool-1-thread-1: is running!
- ----------------CashedThreadPool---------------------
- pool-2-thread-1: is running!
- pool-2-thread-2: is running!
- pool-2-thread-4: is running!
- pool-2-thread-3: is running!
- ----------------SingleThreadExecutor---------------------
- pool-3-thread-1: is running!
- pool-3-thread-1: is running!
- pool-3-thread-1: is running!
- pool-3-thread-1: is running!
復(fù)制代碼 |--Future<T> submit(Callable<T> task): 使用池中已存在的線程或新建一個(gè)線程執(zhí)行task, 與execute()方法不同的是, 該方法會(huì)返回線程的執(zhí)行結(jié)果. submit方法接受一個(gè)Callable<T>對(duì)象, Callable<T>接口是一個(gè)泛型接口, 實(shí)現(xiàn)Callable<T>接口需要重寫(xiě)其中的call()方法, call()方法將返回一個(gè)T對(duì)象. submit方法的返回值是Future<T>對(duì)象, 調(diào)用該對(duì)象的get()可以獲得call()方法的返回值.
Java代碼- 1.public class FutureDemo {
- 2. public static void main(String[] args) throws Exception {
- 3. ExecutorService pool = Executors.newFixedThreadPool(2);
- 4.
- 5. Future<Integer> intFuture = pool.submit(new IntegerCallable());
- 6. // get()方法將阻塞主線程, 直到IntegerCallable線程的call()運(yùn)行結(jié)束并返回結(jié)果時(shí)為止.
- 7. Integer returnInt = intFuture.get();
- 8. System.out.println("返回值為" + returnInt);
- 9.
- 10. Future<Boolean> boolFuture = pool.submit(new BooleanCallable());
- 11. Boolean returnBool = boolFuture.get();
- 12. System.out.println("返回值為" + returnBool);
- 13.
- 14. pool.shutdown();
- 15. }
- 16.
- 17. private final static class IntegerCallable implements Callable<Integer> {
- 18. // call()方法的返回值類型由泛型決定
- 19. @Override
- 20. public Integer call() throws Exception {
- 21. return 2;
- 22. }
- 23. }
- 24.
- 25. private final static class BooleanCallable implements Callable<Boolean> {
- 26. @Override
- 27. public Boolean call() throws Exception {
- 28. return true;
- 29. }
- 30. }
- 31.}
- public class FutureDemo {
- public static void main(String[] args) throws Exception {
- ExecutorService pool = Executors.newFixedThreadPool(2);
-
- Future<Integer> intFuture = pool.submit(new IntegerCallable());
- // get()方法將阻塞主線程, 直到IntegerCallable線程的call()運(yùn)行結(jié)束并返回結(jié)果時(shí)為止.
- Integer returnInt = intFuture.get();
- System.out.println("返回值為" + returnInt);
-
- Future<Boolean> boolFuture = pool.submit(new BooleanCallable());
- Boolean returnBool = boolFuture.get();
- System.out.println("返回值為" + returnBool);
-
- pool.shutdown();
- }
-
- private final static class IntegerCallable implements Callable<Integer> {
- // call()方法的返回值類型由泛型決定
- @Override
- public Integer call() throws Exception {
- return 2;
- }
- }
-
- private final static class BooleanCallable implements Callable<Boolean> {
- @Override
- public Boolean call() throws Exception {
- return true;
- }
- }
- }
復(fù)制代碼 程序的輸出結(jié)果為:
返回值為2
返回值為true
|--List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks): 批量執(zhí)行多個(gè)任務(wù).
Future類. 如果需要獲取線程的執(zhí)行結(jié)果, 那么就會(huì)使用到Future. Future對(duì)象是一個(gè)指向異步執(zhí)行結(jié)果的引用, 由于線程的異步特性, Future對(duì)象在其創(chuàng)建之初可能并不可用, 比如線程的call()方法尚未完成時(shí). 可以調(diào)用Future對(duì)象的isDone()方法判斷線程結(jié)果是否已經(jīng)可用, 在線程結(jié)果返回之前調(diào)用Future對(duì)象的get()方法, 將導(dǎo)致阻塞.
關(guān)閉線程池. 使用完線程池后需要關(guān)閉它, 否則程序可能一直處于運(yùn)行狀態(tài). ExecutorService提供了2個(gè)方法用于關(guān)閉線程池:
|--void shutdown(): 關(guān)閉線程池, 不再接受新任務(wù). 如果存在正在執(zhí)行的任務(wù), 則等待任務(wù)執(zhí)行完成.
|--List<Runnable> shutdownNow(): 關(guān)閉線程池, 不再接受新任務(wù). 盡力嘗試停止正在執(zhí)行的任務(wù), 并返回正在等待的任務(wù)列表.
|--boolean isShutdown(): 判斷線程池是否已經(jīng)關(guān)閉. |
|