學(xué)習(xí)java的同學(xué)注意了!!! 學(xué)習(xí)過程中遇到什么問題或者想獲取學(xué)習(xí)資源的話,歡迎加入Java學(xué)習(xí)交流群,群號(hào)碼:456544752 我們一起學(xué)Java!
在Java1.5中提供了一個(gè)非常高效實(shí)用的多線程包:java.util.concurrent,提供了大量高級(jí)工具,可以幫助開發(fā)者編寫高效易維護(hù)、結(jié)構(gòu)清晰的Java多線程程序。
線程池
之前我們在使用多線程都是用Thread的start()來創(chuàng)建啟動(dòng)一個(gè)線程,但是在實(shí)際開發(fā)中,如果每個(gè)請求到達(dá)就創(chuàng)建一個(gè)新線程,開銷是相當(dāng)大的。服務(wù)器在創(chuàng)建和銷毀線程上花費(fèi)的時(shí)間和消耗的系統(tǒng)資源都相當(dāng)大,甚至可能要比在處理實(shí)際的用請求的時(shí)間和資源要多的多。除了創(chuàng)建和銷毀線程的開銷之外,活動(dòng)的線程也需要消耗系統(tǒng)資源。如果在一個(gè)jvm里創(chuàng)建太多的線程,可能會(huì)使系統(tǒng)由于過度消耗內(nèi)存或“切換過度”而導(dǎo)致系統(tǒng)資源不足。這就引入了線程池概念。
線程池的原理其實(shí)就是對多線程的一個(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)站,那么對于大量的點(diǎn)擊量,就必須要對每一次的點(diǎn)擊做出迅速的處理,這樣才能達(dá)到更好的交互效果…這樣就需要多個(gè)線程去處理這些請求,以便能夠更好的提供服務(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)對突然大爆發(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)建方法
1 ExecutorService executorService1 = Executors.newSingleThreadExecutor();單例線程,表示在任意的時(shí)間段內(nèi),線程池中只有一個(gè)線程在工作…
1 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ì)被移出線程池.
1 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í)間到底有多長.這一類的池子更適用于服務(wù)器.
1 ExecutorService executorService4 = Executors.newCacheThreadPool();調(diào)度型線程池,調(diào)度型線程池會(huì)根據(jù)Scheduled(任務(wù)列表)進(jìn)行延遲執(zhí)行,或者是進(jìn)行周期性的執(zhí)行.適用于一些周期性的工作.
先看一個(gè)簡單例子:
123456789101112131415161718192021 publicclassExecutor {publicstaticvoidmain(String[] args) {//定義了線程池中最大存在的線程數(shù)目ExecutorService executorService=Executors.newFixedThreadPool(10);//添加了一個(gè)任務(wù)...executorService.execute(newRunnable() {@Overridepublicvoidrun() {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方法例子:
12345 publicclassBegincodeimplementsRunnable {publicvoidrun() {System.out.println(“Begincode--runable”);}}三、執(zhí)行任務(wù)通過java.util.concurrent.ExecutorService接口對象來執(zhí)行任務(wù),該對象有兩個(gè)方法可以執(zhí)行任務(wù)execute和submit
12345 publicclassBegincodeimplementscallable{publicvoidcall() {System.out.println(“Begincode--callable”);}}execute這種方式提交沒有返回值,也就不能判斷是否執(zhí)行成功。
submit這種方式它會(huì)返回一個(gè)Future對象,通過future的get方法來獲取返回值,get方法會(huì)阻塞住直到任務(wù)完成。
四、關(guān)閉線程池
當(dāng)我們不需要使用線程池的時(shí)候,我們需要對其進(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地址,并對返回結(jié)果進(jìn)行操作
分析:由于是循環(huán)的對多個(gè)url進(jìn)行訪問并獲取數(shù)據(jù),為了執(zhí)行的效率,考慮使用多線程,url數(shù)量未知如果每個(gè)任務(wù)都創(chuàng)建一個(gè)線程將消耗大量的系統(tǒng)資源,最后決定使用線程池。
給大家貼上代碼
123456789101112131415161718192021222324252627282930313233 publicclassGetMonitorDataService {privateLogger logger = LoggerFactory.getLogger(GetMonitorDataService.class);@ResourceprivateMonitorProjectUrlMapper groupUrlMapper;@ResourceprivateMonitorDetailBatchInsertMapper monitorDetailBatchInsertMapper;publicvoidsendData(){//調(diào)用dao查詢所有urlMonitorProjectUrlExample 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ù)庫取出的urlif(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);}//對數(shù)據(jù)進(jìn)行操作saveData(result);}}任務(wù):
12345678910111213141516 publicclassThreadTaskimplementsRunnable{//這里實(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)行處理@Overridepublicvoidrun() {MonitorDetail detail = HttpClientUtil.send(url, MonitorDetail.class);list.add(detail);}}以上是本人的一些分享,希望和大家多多交流。如果哪里有不足,希望大家指出!
學(xué)習(xí)Java的同學(xué)注意了!!! 學(xué)習(xí)過程中遇到什么問題或者想獲取學(xué)習(xí)資源的話,歡迎加入Java學(xué)習(xí)交流群,群號(hào)碼:456544752 我們一起學(xué)Java!
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注