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