java線程池學習(二) —— 實現一個簡單的線程池

在上一篇文章中《java線程池學習(一) —— BlockingQueue》,我們簡單探討了一下BlockingQueue的概念。

那麼在這邊文章,我們要利用BlockingQueue來自己實現一個簡單的線程池,在以後的章節中,我們再學習一下怎麼去使用java爲我們封裝好的線程池。

首先我們關注一個 “生產者消費者” 的情景。

生產者:不斷產生新的需要解決的任務,比如查詢數據庫,執行某些業務邏輯等。

消費者:不斷解決產生的問題。

那麼把這兩者連接起來的就要用到我們的BlockingQueue了。

生產者將不斷產生的任務放入到隊列中,如果隊列滿了,生產者等待。

消費者不斷的從隊列中取出任務解決,當隊列空了,消費者等待新任務到來。

首先BlockQueue的長度我們要限制,不然如果解決者的解決能力跟不上生產者的,這個任務隊列就會越來越多。

接着我們還需要限定問題解決者的個數,就是我們所謂的線程池中能同時運行的最多的線程數,如果線程數太多的話會嚴重影響系統的穩定性。

那麼我們根據這兩個參數寫一個簡單的線程池:

線程池:

public class ThreadPool {
	//用blockingQueue創建一個任務隊列,初始化長度爲5
	private BlockingQueue<Runnable> tasksQueue = new ArrayBlockingQueue<Runnable>(5);
	//定義線程池中消費者最大數量
	private int consumers = 3;
	
	//這個方法提供給所有的任務生產者,產生新的任務插入
	public void insertTask(Runnable task) throws InterruptedException{
		tasksQueue.put(task);
	}
	
	//線程池的初始化
	ThreadPool(){
                //激活消費者,等待問題到來
		for(int i=1;i<=consumers;i++){
			Solver consumer = new Solver(tasksQueue,i);
			consumer.start();
		}
	}
}

接下來定義 消費者 邏輯:

public class Solver extends Thread{
	
	//引用線程池的任務隊列,消費者不斷的從裏面取得任務去解決
	private BlockingQueue<Runnable> taskQueue = null;
	
	String name;
	
	Solver(BlockingQueue<Runnable> tasks,int name){
		this.taskQueue = tasks;
		this.name = String.valueOf(name);
	}
	
	public void run(){
		try {
			while(true){
				//從隊列中取出任務執行,注意這裏用了take方法,所以如果隊列空了,那麼線程會等待,直到有任務來了,繼續執行
				Runnable task = taskQueue.take();
				System.out.println("消費者"+name+"接收了一個任務");
				task.run();
				System.out.println("消費者"+name+"解決了一個任務");
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

我們在上面的例子可以看到。

這個線程池中最大的線程數是3,就是最多隻能同時有3個消費者線程執行,消費者會監視線程池的任務隊列,只要隊列中有任務,就會取出來執行。

接下來我們定義 生產者 的邏輯:

public class ProblemCreater {

	public static void main(String[] args) throws Exception {
		//初始化線程池
		ThreadPool threadPool = new ThreadPool();
		
		//生成者不斷產生任務
		for(int i=1;i<10;i++){
			
			//定義一個新的任務
			Runnable task = new Runnable(){
				public void run(){
					Random random = new Random();
					//隨機一個數字模擬需要解決的時間
					int randomTime = Math.abs(random.nextInt())%20;
					//System.out.println("這個任務需要解決時間爲:"+randomTime);
					try {
						Thread.sleep(randomTime*1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			};
			//將問題插入到線程池任務隊列中
			threadPool.insertTask(task);
			System.out.println("插入新的任務"+i);
		}
	}

}


到此我們已經實現好了一個非常簡單的線程池,將線程的創建與執行過程分離開,而不是將線程的生命週期管理和任務的執行過程綁定在一起,如果只是想簡單的丟一個任務進去執行,我們只需要將任務的執行過程封裝到一個Runnable接口中就可以了。而對於那些需要返回結果的任務,我們可以將其封裝到Callable接口裏面。

當然java線程池比我們自己寫的這個高大上很多,在以後的篇幅中,我們再做具體研究。



發佈了40 篇原創文章 · 獲贊 32 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章