線程池ExecutorService 一. new Thread的弊端
執(zhí)行一個(gè)異步任務(wù)你還只是如下new Thread嗎?
new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } }).start();
那你就太out了,new Thread的弊端如下:
每次new Thread新建對(duì)象性能差.
線程缺乏統(tǒng)一管理,可能無(wú)限制新建線程,相互之間競(jìng)爭(zhēng),及可能占用過(guò)多系統(tǒng)資源導(dǎo)致死機(jī)或oom.
缺乏更多功能,如定時(shí)執(zhí)行、定期執(zhí)行、線程中斷.
相比new Thread,Java提供的四種線程池的好處在于:
重用存在的線程,減少對(duì)象創(chuàng)建、消亡的開(kāi)銷,性能佳.
可有效控制最大并發(fā)線程數(shù),提高系統(tǒng)資源的使用率,同時(shí)避免過(guò)多資源競(jìng)爭(zhēng),避免堵塞.
提供定時(shí)執(zhí)行、定期執(zhí)行、單線程、并發(fā)數(shù)控制等功能.
二. ExecutorService詳解Java通過(guò)Executors提供四種線程池,分別為:
newCachedThreadPool創(chuàng)建一個(gè)可緩存線程池,如果線程池長(zhǎng)度超過(guò)處理需要,可靈活回收空閑線程,若無(wú)可回收,則新建線程.
newFixedThreadPool 創(chuàng)建一個(gè)定長(zhǎng)線程池,可控制線程最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待.
newScheduledThreadPool 創(chuàng)建一個(gè)定長(zhǎng)線程池,支持定時(shí)及周期性任務(wù)執(zhí)行.
newSingleThreadExecutor 創(chuàng)建一個(gè)單線程化的線程池,它只會(huì)用唯一的工作線程來(lái)執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級(jí))執(zhí)行.
01. submit()與execute()區(qū)別接收的參數(shù)不一樣 submit()可以接受runnable和callable 有返回值execute()接受runnable 無(wú)返回值
submit有返回值,而execute沒(méi)有
Method submit extends base method Executor.execute by creating and returning a Future that can be used to cancel execution and/or wait for completion.
用到返回值的例子,比如說(shuō)我有很多個(gè)做validation的task,我希望所有的task執(zhí)行完,然后每個(gè)task告訴我它的執(zhí)行結(jié)果,是成功還是失敗,如果是失敗,原因是什么.
submit方便Exception處理
There is a difference when looking at exception handling. If your tasks throws an exception and if it was submitted with execute this exception will Go to the uncaught exception handler (when you don’t have provided one explicitly, the default one will just print the stack trace to System.err). If you submitted the task with submit any thrown exception, checked or not, is then part of the task’s return status. For a task that was submitted with submit and that terminates with an exception, the Future.get will rethrow this exception, wrapped in an ExecutionException.
意思就是如果你在你的task里會(huì)拋出checked或者unchecked exception,而你又希望外面的調(diào)用者能夠感知這些exception并做出及時(shí)的處理,那么就需要用到submit,通過(guò)捕獲Future.get拋出的異常.
import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class ExecutorServiceTest { public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); List> resultList = new ArrayList >(); // 創(chuàng)建10個(gè)任務(wù)并執(zhí)行 for (int i = 0; i < 10; i++) { // 使用ExecutorService執(zhí)行Callable類型的任務(wù),并將結(jié)果保存在future變量中 Future future = executorService.submit(new TaskWithResult(i)); // 將任務(wù)執(zhí)行結(jié)果存儲(chǔ)到List中 resultList.add(future); } executorService.shutdown(); // 遍歷任務(wù)的結(jié)果 for (Future fs : resultList) { try { System.out.println(fs.get()); // 打印各個(gè)線程(任務(wù))執(zhí)行的結(jié)果 } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { executorService.shutdownNow(); e.printStackTrace(); return; } } } } class TaskWithResult implements Callable { private int id; public TaskWithResult(int id) { this.id = id; } /** * 任務(wù)的具體過(guò)程,一旦任務(wù)傳給ExecutorService的submit方法,則該方法自動(dòng)在一個(gè)線程上執(zhí)行. * * @return * @throws Exception */ public String call() throws Exception { System.out.println("call()方法被自動(dòng)調(diào)用,干活!!! " + Thread.currentThread().getName()); if (new Random().nextBoolean()) throw new TaskException("Meet error in task." + Thread.currentThread().getName()); // 一個(gè)模擬耗時(shí)的操作 for (int i = 999999999; i > 0; i--) ; return "call()方法被自動(dòng)調(diào)用,任務(wù)的結(jié)果是:" + id + " " + Thread.currentThread().getName(); } } class TaskException extends Exception { public TaskException(String message) { super(message); } }
執(zhí)行的結(jié)果類似于:
call()方法被自動(dòng)調(diào)用,干活!!! pool-1-thread-1 call()方法被自動(dòng)調(diào)用,干活!!! pool-1-thread-2 call()方法被自動(dòng)調(diào)用,干活!!! pool-1-thread-3 call()方法被自動(dòng)調(diào)用,干活!!! pool-1-thread-5 call()方法被自動(dòng)調(diào)用,干活!!! pool-1-thread-7 call()方法被自動(dòng)調(diào)用,干活!!! pool-1-thread-4 call()方法被自動(dòng)調(diào)用,干活!!! pool-1-thread-6 call()方法被自動(dòng)調(diào)用,干活!!! pool-1-thread-7 call()方法被自動(dòng)調(diào)用,干活!!! pool-1-thread-5 call()方法被自動(dòng)調(diào)用,干活!!! pool-1-thread-8 call()方法被自動(dòng)調(diào)用,任務(wù)的結(jié)果是:0 pool-1-thread-1 call()方法被自動(dòng)調(diào)用,任務(wù)的結(jié)果是:1 pool-1-thread-2 java.util.concurrent.ExecutionException: com.cicc.pts.TaskException: Meet error in task.pool-1-thread-3 at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222) at java.util.concurrent.FutureTask.get(FutureTask.java:83) at com.cicc.pts.ExecutorServiceTest.main(ExecutorServiceTest.java:29) Caused by: com.cicc.pts.TaskException: Meet error in task.pool-1-thread-3 at com.cicc.pts.TaskWithResult.call(ExecutorServiceTest.java:57) at com.cicc.pts.TaskWithResult.call(ExecutorServiceTest.java:1) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:619)
可以看見(jiàn)一旦某個(gè)task出錯(cuò),其它的task就停止執(zhí)行.
可以關(guān)閉 ExecutorService,這將導(dǎo)致其拒絕新任務(wù).提供兩個(gè)方法來(lái)關(guān)閉 ExecutorService.
shutdown() 方法在終止前允許執(zhí)行以前提交的任務(wù),
shutdownNow() 方法阻止等待任務(wù)啟動(dòng)并試圖停止當(dāng)前正在執(zhí)行的任務(wù).在終止時(shí)執(zhí)行程序沒(méi)有任務(wù)在執(zhí)行,也沒(méi)有任務(wù)在等待執(zhí)行,并且無(wú)法提交新任務(wù).關(guān)閉未使用的 ExecutorService 以允許回收其資源.
一般分兩個(gè)階段關(guān)閉 ExecutorService.第一階段調(diào)用 shutdown 拒絕傳入任務(wù),然后調(diào)用 shutdownNow(如有必要)取消所有遺留的任務(wù)
// 啟動(dòng)一次順序關(guān)閉,執(zhí)行以前提交的任務(wù),但不接受新任務(wù).
threadPool.shutdown();
03. Runnable()與Callable()區(qū)別
如果是一個(gè)多線程協(xié)作程序,比如菲波拉切數(shù)列,1,1,2,3,5,8…使用多線程來(lái)計(jì)算.
但后者需要前者的結(jié)果,就需要用callable接口了.
callable用法和runnable一樣,只不過(guò)調(diào)用的是call方法,該方法有一個(gè)泛型返回值類型,你可以任意指定.
runnable接口實(shí)現(xiàn)的沒(méi)有返回值的并發(fā)編程.
callable實(shí)現(xiàn)的存在返回值的并發(fā)編程.(call的返回值String受泛型的影響) 使用Future獲取返回值.
這里寫圖片描述
創(chuàng)建一個(gè)可緩存線程池,如果線程池長(zhǎng)度超過(guò)處理需要,可靈活回收空閑線程,若無(wú)可回收,則新建線程.示例代碼如下:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { final int index = i; try { Thread.sleep(index * 1000); } catch (InterruptedException e) { e.printStackTrace(); } cachedThreadPool.execute(new Runnable() { @Override public void run() { System.out.println(index); } }); }
線程池為無(wú)限大,當(dāng)執(zhí)行第二個(gè)任務(wù)時(shí)第一個(gè)任務(wù)已經(jīng)完成,會(huì)復(fù)用執(zhí)行第一個(gè)任務(wù)的線程,而不用每次新建線程.
02. newFixedThreadPool創(chuàng)建一個(gè)定長(zhǎng)線程池,可控制線程最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待.示例代碼如下:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); for (int i = 0; i < 10; i++) { final int index = i; fixedThreadPool.execute(new Runnable() { @Override public void run() { try { System.out.println(index); Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); }
因?yàn)榫€程池大小為3,每個(gè)任務(wù)輸出index后sleep 2秒,所以每?jī)擅氪蛴?個(gè)數(shù)字.
定長(zhǎng)線程池的大小最好根據(jù)系統(tǒng)資源進(jìn)行設(shè)置.如Runtime.getRuntime().availableProcessors().可參考PreloadDataCache.
創(chuàng)建一個(gè)定長(zhǎng)線程池,支持定時(shí)及周期性任務(wù)執(zhí)行.延遲執(zhí)行示例代碼如下:
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); scheduledThreadPool.schedule(new Runnable() { @Override public void run() { System.out.println("delay 3 seconds"); } }, 3, TimeUnit.SECONDS);
表示延遲3秒執(zhí)行.
定期執(zhí)行示例代碼如下:
scheduledThreadPool.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println("delay 1 seconds, and excute every 3 seconds"); } }, 1, 3, TimeUnit.SECONDS);
表示延遲1秒后每3秒執(zhí)行一次.
ScheduledExecutorService比Timer更安全,功能更強(qiáng)大,后面會(huì)有一篇多帶帶進(jìn)行對(duì)比.
創(chuàng)建一個(gè)單線程化的線程池,它只會(huì)用唯一的工作線程來(lái)執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級(jí))執(zhí)行.示例代碼如下:
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { final int index = i; singleThreadExecutor.execute(new Runnable() { @Override public void run() { try { System.out.println(index); Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); }
結(jié)果依次輸出,相當(dāng)于順序執(zhí)行各個(gè)任務(wù).
現(xiàn)行大多數(shù)GUI程序都是單線程的.Android中單線程可用于數(shù)據(jù)庫(kù)操作,文件操作,應(yīng)用批量安裝,應(yīng)用批量刪除等不適合并發(fā)但可能IO阻塞性及影響UI線程響應(yīng)的操作.
總結(jié):
使用ExecutorService的submit函數(shù)由于execute函數(shù)
異常如何處理,異常后其他task停止
四. 線程安全問(wèn)題 01. 日期類型轉(zhuǎn)換SimpleDateFormat來(lái)做Date到String的類型轉(zhuǎn)換,建議使用Apache commons-lang中的FastDateFormat。
因?yàn)镴DK里自帶的SimpleDateFormat存在線程不安全問(wèn)題。
maven依賴:
commons-lang commons-lang 2.5
代碼:
private String initDate() { Date d = new Date(); FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss"); return fdf.format(d); }
多線程問(wèn)題,主要是多線程執(zhí)行時(shí)的順序是隨機(jī)的,無(wú)法保證同一代碼的執(zhí)行順序,任意兩步代碼(非原子)操作都存在安全問(wèn)題
02. 線程安全類型多線程問(wèn)題,主要是多線程執(zhí)行時(shí)的順序是隨機(jī)的,無(wú)法保證同一代碼的執(zhí)行順序,任意兩步代碼(非原子)操作都存在安全問(wèn)題(01). 鎖在String
String str="a"; synchronized(str){ str = "b";? //str變?yōu)橐粋€(gè)新對(duì)象,鎖失效,字符的賦值是新new一個(gè)String然后賦值的 }(02). AtomicInteger
i++,使用java.util.concurrent.atomic下的原子類代替來(lái)做多線程的計(jì)數(shù)器
i++是兩步,讀取i的變量的值,然后更新+1,所以不安全, 使用AtomicInteger
HashMap,ArrayList,使用ConcurrentHashMap,CopyOnWriteArrayList代替
(04). StringBufferStringBuilder,使用StringBuffer代替
參考引用:
https://www.cnblogs.com/Steve...
https://blog.csdn.net/insistg...
https://blog.csdn.net/c511362...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/73222.html
摘要:線程池常見(jiàn)實(shí)現(xiàn)線程池一般包含三個(gè)主要部分調(diào)度器決定由哪個(gè)線程來(lái)執(zhí)行任務(wù)執(zhí)行任務(wù)所能夠的最大耗時(shí)等線程隊(duì)列存放并管理著一系列線程這些線程都處于阻塞狀態(tài)或休眠狀態(tài)任務(wù)隊(duì)列存放著用戶提交的需要被執(zhí)行的任務(wù)一般任務(wù)的執(zhí)行的即先提交的任務(wù)先被執(zhí)行調(diào)度 線程池常見(jiàn)實(shí)現(xiàn) 線程池一般包含三個(gè)主要部分: 調(diào)度器: 決定由哪個(gè)線程來(lái)執(zhí)行任務(wù), 執(zhí)行任務(wù)所能夠的最大耗時(shí)等 線程隊(duì)列: 存放并管理著一系列線...
摘要:并發(fā)包參考多線程的同步協(xié)助同步控制擴(kuò)展功能重入鎖之前重入鎖性能好于但開(kāi)始優(yōu)化現(xiàn)在二者的性能相差不大。倒計(jì)時(shí)器的擴(kuò)展循柵欄。做好異常處理工作。線程池的內(nèi)部實(shí)現(xiàn)該部分待看書 JDK 并發(fā)包 參考:> https://github.com/chengbingh... 3.1 多線程的同步協(xié)助:同步控制 3.1.1 synchronized 擴(kuò)展功能:重入鎖jdk1.5之前重入鎖Reentra...
摘要:并發(fā)編程實(shí)戰(zhàn)水平很高,然而并不是本好書。一是多線程的控制,二是并發(fā)同步的管理。最后,使用和來(lái)關(guān)閉線程池,停止其中的線程。當(dāng)線程調(diào)用或等阻塞時(shí),對(duì)這個(gè)線程調(diào)用會(huì)使線程醒來(lái),并受到,且線程的中斷標(biāo)記被設(shè)置。 《Java并發(fā)編程實(shí)戰(zhàn)》水平很高,然而并不是本好書。組織混亂、長(zhǎng)篇大論、難以消化,中文翻譯也較死板。這里是一篇批評(píng)此書的帖子,很是貼切。俗話說(shuō):看到有這么多人罵你,我就放心了。 然而知...
摘要:本文主要內(nèi)容為簡(jiǎn)單總結(jié)中線程池的相關(guān)信息。方法簇方法簇用于創(chuàng)建固定線程數(shù)的線程池。三種常見(jiàn)線程池的對(duì)比上文總結(jié)了工具類創(chuàng)建常見(jiàn)線程池的方法,現(xiàn)對(duì)三種線程池區(qū)別進(jìn)行比較。 概述 線程可認(rèn)為是操作系統(tǒng)可調(diào)度的最小的程序執(zhí)行序列,一般作為進(jìn)程的組成部分,同一進(jìn)程中多個(gè)線程可共享該進(jìn)程的資源(如內(nèi)存等)。在單核處理器架構(gòu)下,操作系統(tǒng)一般使用分時(shí)的方式實(shí)現(xiàn)多線程;在多核處理器架構(gòu)下,多個(gè)線程能夠...
摘要:創(chuàng)建一個(gè)定長(zhǎng)線程池,可控制線程最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待。創(chuàng)建一個(gè)定長(zhǎng)線程池,支持定時(shí)及周期性任務(wù)執(zhí)行。 ExecutorService是Java中對(duì)線程池定義的一個(gè)接口,它java.util.concurrent包中. 創(chuàng)建一個(gè)什么樣的ExecutorService的實(shí)例(即線程池)需要g根據(jù)具體應(yīng)用場(chǎng)景而定,不過(guò)Java給我們提供了一個(gè)Executors工廠類,它可以幫助...
閱讀 1837·2021-11-11 16:55
閱讀 759·2019-08-30 15:53
閱讀 3598·2019-08-30 15:45
閱讀 746·2019-08-30 14:10
閱讀 3275·2019-08-30 12:46
閱讀 2132·2019-08-29 13:15
閱讀 2034·2019-08-26 13:48
閱讀 942·2019-08-26 12:23