在java1.5中提供了一個(gè)非常高效實(shí)用的多線程包:java.util.concurrent,提供了大量高級(jí)工具,可以幫助開發(fā)者編寫高效易維護(hù)、結(jié)構(gòu)清晰的Java多線程程序。
線程池
之前我們?cè)谑褂枚嗑€程都是用Thread的start()來創(chuàng)建啟動(dòng)一個(gè)線程,但是在實(shí)際開發(fā)中,如果每個(gè)請(qǐng)求到達(dá)就創(chuàng)建一個(gè)新線程,開銷是相當(dāng)大的。服務(wù)器在創(chuàng)建和銷毀線程上花費(fèi)的時(shí)間和消耗的系統(tǒng)資源都相當(dāng)大,甚至可能要比在處理實(shí)際的用請(qǐng)求的時(shí)間和資源要多的多。除了創(chuàng)建和銷毀線程的開銷之外,活動(dòng)的線程也需要消耗系統(tǒng)資源。如果在一個(gè)jvm里創(chuàng)建太多的線程,可能會(huì)使系統(tǒng)由于過度消耗內(nèi)存或“切換過度”而導(dǎo)致系統(tǒng)資源不足。這就引入了線程池概念。
線程池的原理其實(shí)就是對(duì)多線程的一個(gè)管理,為了實(shí)現(xiàn)異步機(jī)制的一種方法,其實(shí)就是多個(gè)線程執(zhí)行多個(gè)任務(wù),最終這些線程通過線程池進(jìn)行管理…不用手動(dòng)去維護(hù)…一次可以處理多個(gè)任務(wù),這樣就可以迅速的進(jìn)行相應(yīng)…比如說一個(gè)網(wǎng)站成為了熱點(diǎn)網(wǎng)站,那么對(duì)于大量的點(diǎn)擊量,就必須要對(duì)每一次的點(diǎn)擊做出迅速的處理,這樣才能達(dá)到更好的交互效果…這樣就需要多個(gè)線程去處理這些請(qǐng)求,以便能夠更好的提供服務(wù)…
在java.util.concurrent包下,提供了一系列與線程池相關(guān)的類。合理的使用線程池,可以帶來多個(gè)好處:
(1)降低資源消耗。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗;
(2)提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行;
(3)提高線程的可管理性。線程是稀缺資源,如果無限制的創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。
線程池可以應(yīng)對(duì)突然大爆發(fā)量的訪問,通過有限個(gè)固定線程為大量的操作服務(wù),減少創(chuàng)建和銷毀線程所需的時(shí)間。
使用線程池:
1.創(chuàng)建線程池
2.創(chuàng)建任務(wù)
3.執(zhí)行任務(wù)
4.關(guān)閉線程池
一、創(chuàng)建線程池的方法
我們一般通過工具類Executors的靜態(tài)方法來獲取線程池或靜態(tài)方法。介紹四種常用創(chuàng)建方法
ExecutorService executorService1 = Executors.newSingleThreadExecutor();單例線程,表示在任意的時(shí)間段內(nèi),線程池中只有一個(gè)線程在工作…
ExecutorService executorService2 = Executors.newFixedThreadPool(10);緩存線程池,先查看線程池中是否有當(dāng)前執(zhí)行線程的緩存,如果有就resue(復(fù)用),如果沒有,那么需要?jiǎng)?chuàng)建一個(gè)線程來完成當(dāng)前的調(diào)用.并且這類線程池只能完成一些生存期很短的一些任務(wù).并且這類線程池內(nèi)部規(guī)定能resue(復(fù)用)的線程,空閑的時(shí)間不能超過60s,一旦超過了60s,就會(huì)被移出線程池.ExecutorService executorService3 = Executors.newScheduledThreadPool(10);固定型線程池,和newCacheThreadPool()差不多,也能夠?qū)崿F(xiàn)resue(復(fù)用),但是這個(gè)池子規(guī)定了線程的最大數(shù)量,也就是說當(dāng)池子有空閑時(shí),那么新的任務(wù)將會(huì)在空閑線程中被執(zhí)行,一旦線程池內(nèi)的線程都在進(jìn)行工作,那么新的任務(wù)就必須等待線程池有空閑的時(shí)候才能夠進(jìn)入線程池,其他的任務(wù)繼續(xù)排隊(duì)等待.這類池子沒有規(guī)定其空閑的時(shí)間到底有多長(zhǎng).這一類的池子更適用于服務(wù)器.ExecutorService executorService4 = Executors.newCacheThreadPool();調(diào)度型線程池,調(diào)度型線程池會(huì)根據(jù)Scheduled(任務(wù)列表)進(jìn)行延遲執(zhí)行,或者是進(jìn)行周期性的執(zhí)行.適用于一些周期性的工作.先看一個(gè)簡(jiǎn)單例子:
publicclass Executor { publicstatic void main(String[] args) { //定義了線程池中最大存在的線程數(shù)目 ExecutorService executorService=Executors.newFixedThreadPool(10); //添加了一個(gè)任務(wù)... executorService.execute(newRunnable() { @Override publicvoid run() { while(true){ System.out.PRintln("begincode"); try{ Thread.sleep(1000); }catch(InterruptedException e) { e.printStackTrace(); } } } }); }}執(zhí)行結(jié)果就是無限循環(huán)每隔1秒打印一個(gè)begincode…二、創(chuàng)建任務(wù)
任務(wù)分為兩種一種是有返回值的,一種是沒有返回值的無返回值的任務(wù)就是一個(gè)實(shí)現(xiàn)了runnable接口的類.使用run方法有返回值的任務(wù)是一個(gè)實(shí)現(xiàn)了callable接口的類.使用call方法例子:publicclassBegincode implementsRunnable { publicvoidrun() { System.out.println(“Begincode--runable”); } }publicclassBegincode implementscallable{ publicvoidcall() { System.out.println(“Begincode--callable”); } }三、執(zhí)行任務(wù)通過java.util.concurrent.ExecutorService接口對(duì)象來執(zhí)行任務(wù),該對(duì)象有兩個(gè)方法可以執(zhí)行任務(wù)execute和submitexecute這種方式提交沒有返回值,也就不能判斷是否執(zhí)行成功。
submit這種方式它會(huì)返回一個(gè)Future對(duì)象,通過future的get方法來獲取返回值,get方法會(huì)阻塞住直到任務(wù)完成。
四、關(guān)閉線程池
當(dāng)我們不需要使用線程池的時(shí)候,我們需要對(duì)其進(jìn)行關(guān)閉…有兩種方法可以關(guān)閉掉線程池…
shutdown()
shutdown并不是直接關(guān)閉線程池,而是不再接受新的任務(wù)…如果線程池內(nèi)有任務(wù),那么把這些任務(wù)執(zhí)行完畢后,關(guān)閉線程池….
shutdownNow()
這個(gè)方法表示不再接受新的任務(wù),并把任務(wù)隊(duì)列中的任務(wù)直接移出掉,如果有正在執(zhí)行的,嘗試進(jìn)行停止…
案例分享
需求:從數(shù)據(jù)庫中獲取url,并利用httpclient循環(huán)訪問url地址,并對(duì)返回結(jié)果進(jìn)行操作
分析:由于是循環(huán)的對(duì)多個(gè)url進(jìn)行訪問并獲取數(shù)據(jù),為了執(zhí)行的效率,考慮使用多線程,url數(shù)量未知如果每個(gè)任務(wù)都創(chuàng)建一個(gè)線程將消耗大量的系統(tǒng)資源,最后決定使用線程池。
給大家貼上代碼
publicclass GetMonitorDataService { privateLogger logger = LoggerFactory.getLogger(GetMonitorDataService.class); @Resource privateMonitorProjectUrlMapper groupUrlMapper; @Resource privateMonitorDetailBatchInsertMapper monitorDetailBatchInsertMapper; publicvoid sendData(){ //調(diào)用dao查詢所有url MonitorProjectUrlExample example=newMonitorProjectUrlExample(); List<MonitorProjectUrl> list=groupUrlMapper.selectByExample(example); logger.info("此次查詢數(shù)據(jù)庫中監(jiān)控url個(gè)數(shù)為"+list.size()); //獲取系統(tǒng)處理器個(gè)數(shù),作為線程池?cái)?shù)量 intnThreads=Runtime.getRuntime().availableProcessors(); //定義一個(gè)裝載多線程返回值的集合 List<MonitorDetail> result= Collections.synchronizedList(newArrayList<MonitorDetail>()); //創(chuàng)建線程池,這里定義了一個(gè)創(chuàng)建線程池的工具類,避免了創(chuàng)建多個(gè)線程池 ExecutorService executorService = ThreadPoolFactoryUtil.getExecutorService(nThreads); //遍歷數(shù)據(jù)庫取出的url if(list!=null&&list.size()>0) { for(MonitorProjectUrl monitorProjectUrl : list) { String url = monitorProjectUrl.getMonitorUrl(); //創(chuàng)建任務(wù) ThreadTask threadTask = newThreadTask(url, result); //執(zhí)行任務(wù) executorService.execute(threadTask); } //對(duì)數(shù)據(jù)進(jìn)行操作 saveData(result); } }任務(wù):publicclass ThreadTask implementsRunnable{ //這里實(shí)現(xiàn)runnable接口 privateString url; privateList<MonitorDetail> list; publicThreadTask(String url,List<MonitorDetail> list){ this.url=url; this.list=list; } //把獲取的數(shù)據(jù)進(jìn)行處理 @Override publicvoid run() { MonitorDetail detail = HttpClientUtil.send(url, MonitorDetail.class); list.add(detail); } }以上是本人的一些分享,希望和大家多多交流。如果哪里有不足,希望大家指出!
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注