1. 程式人生 > >執行緒池的簡介及四種建立方法

執行緒池的簡介及四種建立方法

適用場景

像Web伺服器、資料庫伺服器、檔案伺服器、郵件伺服器等伺服器應用程式都是處理來自某些遠端來源的大量短小的任務。伺服器經常出現的情況是:單個任務處理的時間很短而請求的數量巨大。

簡單的構建方式

每當一個請求到達就為其建立一個新執行緒,然後在新執行緒中為其服務。

優點:適用於原型開發

缺點:用於伺服器存在諸多問題,為每個請求建立一個新執行緒的開銷很大;花費在建立和銷燬新執行緒上的時間和資源比花在處理實際的使用者請求上的時間和資源更多;除了建立和銷燬執行緒的開銷之外,活動的執行緒也消耗資源,在JVM中建立過多的執行緒會導致系統過度消耗記憶體而用完記憶體或者"切換過度"。

執行緒池的作用

作用

:主要用來解決執行緒生命週期開銷問題和系統資源不足的問題。通過對多個任務重用執行緒,執行緒建立的開銷就分攤到多個任務上了,而且由於在請求到達時執行緒已經存在,所以消除了建立執行緒帶來的延遲。

優點:能立即為請求服務,提高了響應速度;可以通過調整線池中執行緒的數目防止出現資源不足的情況。

缺點:使用執行緒池構建的應用程式和其他多執行緒應用程式一樣容易遭受併發錯誤,如同步錯誤和死鎖;還容易遭受特定於執行緒池的其他少數風險,如與池有關的死鎖,資源不足和執行緒洩漏、請求過載。

併發錯誤:執行緒池和其他併發機制依靠外套wait()和notify()方法,如果編碼不正確,那麼可能丟失通知,導致執行緒保持空閒狀態。

死鎖:一般的死鎖,滿足死鎖的四個必要條件;池死鎖,執行緒池中的所以執行緒都在執行已阻塞的等待佇列中另一任務的執行結果的任務,而這個任務因為沒有可以佔用的執行緒而無法執行。

資源不足:如果執行緒池過大,被執行緒消耗的資源可能會嚴重影響系統性能;雖然執行緒之間切換的排程開銷很小,但如果有很多執行緒,那麼切換可能會嚴重地影響程式的效能。

執行緒洩漏:當從池中除去一個執行緒以執行任務,而任務結束後執行緒沒有返回執行緒池。當任務丟擲一個RuntimeException或一個Error時,如果池類沒有捕獲他們,那麼執行緒只會退出執行緒池大小將永久減一,發生次數過多將導致執行緒池為空,系統將停止,沒有執行緒可供使用;有些執行緒會永遠等待某些資源或者使用者輸入,而這些資源不能保證變得可用,或者使用者已經離開,如果一個執行緒永久的消耗著,那麼相當於從池中除去,應該要麼只給予它們自己的執行緒,要麼只讓它們等待有限的時間。

請求過載:請求壓垮伺服器。可以簡單地拋棄請求,依靠更高級別的協議稍後重試請求;也可以用伺服器暫時很忙的響應來拒絕請求。

執行緒池的建立

一、建立單執行緒的執行緒池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/*
 * 建立只有一個執行緒的執行緒池
 */
public class Single {
	public static void main(String[] args){
		ExecutorService ex = Executors.newSingleThreadExecutor();
		for(int i=0; i<5; i++){
			runnable rn = new runnable();
			ex.execute(rn);
		}
		ex.shutdown();
	}
}
class runnable implements Runnable{
	public void run(){
		System.out.println(Thread.currentThread().getName());
	}
}

二、建立固定數量的執行緒池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/*
 * 建立具一個可重用的,有固定數量的執行緒池
 * 每次提交一個任務就提交一個執行緒,直到執行緒達到線城池大小,就不會建立新執行緒了
 * 執行緒池的大小達到最大後達到穩定不變,如果一個執行緒異常終止,則會建立新的執行緒
 */
public class newThreadpool {
	private static int pool = 2;
	public static void main(String[] args){
		ExecutorService ex = Executors.newFixedThreadPool(pool);
		for(int i=0; i<5; i++){
			runnable rn = new runnable();
			ex.execute(rn);
		}
		ex.shutdown();
	}
}

三、建立數量不固定的執行緒池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/* 
 * 具有緩衝功能的執行緒池,系統根據需要建立執行緒,執行緒會被緩衝到執行緒池中
 * 如果執行緒池大小超過了處理任務所需要的執行緒執行緒池就會回收空閒的執行緒池,
 * 當處理任務增加時,執行緒池可以增加執行緒來處理任務執行緒池不會對執行緒的大
 * 小進行限制執行緒池的大小依賴於作業系統
 */
public class Cached {
	public static void main(String[] args){
		ExecutorService ex = Executors.newCachedThreadPool();
		for(int i=0; i<5; i++){
			runnable rn = new runnable();
			ex.execute(rn);
		}
		ex.shutdown();
	}
}

四、建立定時執行緒池

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/*
 * 建立一個執行緒池,大小可以設定,此執行緒支援定時以及週期性的執行任務定時任務
 */
public class Scheduled {
	private static int pool = 2;
	public static void main(String[] args){
		ScheduledExecutorService ex = Executors.newScheduledThreadPool(pool);
		runnable rn = new runnable();
		//引數1:目標物件   引數2:隔多長時間開始執行執行緒,    引數3:執行週期       引數4:時間單位
        ex.scheduleAtFixedRate(rn, 3, 1, TimeUnit.MILLISECONDS);
	}
}