線程池是Java5提供的一個新技術,方便我們快速簡潔的定義線程池。包括如下:
諸如 Web 服務器、數據庫服務器、文件服務器或郵件服務器之類的許多服務器應用程序都面向處理來自某些遠程來源的大量短小的任務。請求以某種方式到達服務器,這種方式可能是通過網絡協議(例如 HTTP、FTP 或 POP)、通過 JMS 隊列或者可能通過輪詢數據庫。不管請求如何到達,服務器應用程序中經常出現的情況是:單個任務處理的時間很短而請求的數目卻是巨大的。
構建服務器應用程序的一個過於簡單的模型應該是:每當一個請求到達就創建一個新線程,然後在新線程中為請求服務。實際上,對於原型開發這種方法工作得很好,但如果試圖部署以這種方式運行的服務器應用程序,那麼這種方法的嚴重不足就很明顯。每個請求對應一個線程(thread-per-request)方法的不足之一是:為每個請求創建一個新線程的開銷很大;為每個請求創建新線程的服務器在創建和銷毀線程上花費的時間和消耗的系統資源要比花在處理實際的用戶請求的時間和資源更多。
除了創建和銷毀線程的開銷之外,活動的線程也消耗系統資源。在一個 JVM 裡創建太多的線程可能會導致系統由於過度消耗內存而用完內存或“切換過度”。為了防止資源不足,服務器應用程序需要一些辦法來限制任何給定時刻處理的請求數目。
線程池為線程生命周期開銷問題和資源不足問題提供了解決方案。通過對多個任務重用線程,線程創建的開銷被分攤到了多個任務上。其好處是,因為在請求到達時線程已經存在,所以無意中也消除了線程創建所帶來的延遲。這樣,就可以立即為請求服務,使應用程序響應更快。而且,通過適當地調整線程池中的線程數目,也就是當請求的數目超過某個阈值時,就強制其它任何新到的請求一直等待,直到獲得一個線程來處理為止,從而可以防止資源不足。
Java5提供以下線程池:
緩存線程池(newCachedThreadPool),可以創建任意個線程,每個任務過來後都會創建一個線程,用於任務少,或執行時間短的任務,例如我們創建十個任務,那麼緩沖線程池將會創建十個線程來執行。如下代碼:
- ExecutorService threadPool = Executors.newCachedThreadPool();
- for(int i=1; i<=10; i++){
- final int taskId = i;
- threadPool.execute(new Runnable(){
- public void run() {
- for(int i=1; i<=10; i++){
- System.out.println(Thread.currentThread().getName() + " is looping of " + i + " the task is " + taskId);
- try {
- Thread.sleep(20);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- });
- }
- System.out.println("add all of 10 task");
- threadPool.shutdown();
固定數量線程池(newFixedThreadPool)允許我們創建固定線程數量的線程池,如果任務數大於線程池中線程的數量,那麼任務將等待,如下代碼:
- ExecutorService threadPool = Executors.newFixedThreadPool(3);
- for(int i=1; i<=10; i++){
- final int taskId = i;
- threadPool.execute(new Runnable(){
- public void run() {
- for(int i=1; i<=10; i++){
- System.out.println(Thread.currentThread().getName() + " is looping of " + i + " the task is " + taskId);
- try {
- Thread.sleep(20);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- });
- }
- System.out.println("add all of 10 task");
- threadPool.shutdown();
如何實現線程掛掉後重新啟動(創建單一的線程池)newSingleThreadExecutor(),這樣線程池中只會有一個線程工作,當線程失敗後會重新創建一個線程將失敗的線程替換掉。
定時器線程池(scheduleAtFixedRate)與定時器很類似,可以指定線程池中線程在多長時間後執行,以及每個多長時間執行一次,代碼如下,可以模擬讓炸彈在6s後爆炸,並且每個2s炸一次:
- Executors.newScheduledThreadPool(3).scheduleAtFixedRate(
- // .schedule(
- new Runnable(){
- public void run() {
- System.out.println("boming");
- }
- }, 6, 2, TimeUnit.SECONDS);
- }
大家可以執行代碼測試。