10.1 爲什麼使用線程池(Why Thread Pools?)
線程池在java多線程開發中用的非常普遍,它裏面存放了一定數量的Thread,當有任務需要執行的時候,(通常是Runnable對象),就從線程池裏面拿出空閒的Thread來執行任務,執行完了之後再把Thread放回去等待下一個任務。
使用線程池的理由如下
1)創建一個Thread的開銷是很大的,使用線程池可以省去這部分的開銷,對有些對時間敏感的工程來說,是必須的
2)第二個原因是方便我們的程序設計,如果你有太多的線程需要你去維護,這是一個很繁瑣的工程,使用線程池可以你管理,讓你把精力投入到具體的邏輯當中
3)第三個原因是線程池可以讓多個線程併發運行
這裏需要說明一下併發和並行的區別,併發就好比一個人同時吃三個饅頭,並行相當於三個人吃三個饅頭,專業一點就是,併發是一個CPU運行多個任務,並行是多個CPU運行多個任務
爲了方便理解,我模擬了一個線程池
package com.yellow; import java.util.LinkedList; /** * 線程池的模擬 * * @author yellowbaby * */ public class ThreadPool extends ThreadGroup { /** * 用來存放Task的工作隊列 */ private LinkedList<Runnable> workQueue = new LinkedList<Runnable>(); public ThreadPool(int poolSize) { super("");// 父類無參數的構造函數是private的 startWorkThreads(poolSize); } /** * 開啓工作線程 * @param poolSize */ private void startWorkThreads(int poolSize) { for (int i = 0; i < poolSize; i++) { new WorkThread(i).start(); } } /** * 執行任務 * @param task */ public synchronized void execute(Runnable task) { if (task != null) { workQueue.add(task);//往工作隊列裏面添加一個任務 notify();//喚醒一個正在等待的工作線程 } } /** * 工作線程從工作隊列裏面取task,如果工作隊列爲空,就等待 */ private synchronized Runnable getTask() { while (workQueue.size() == 0) { try { System.out.println("工作線程 " + Thread.currentThread().getName() + " 正在等待任務"); this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } return workQueue.removeFirst(); } /** * 工作線程,從隊列裏面取Task,取到了就執行,沒有就等待 */ private class WorkThread extends Thread { public WorkThread(int id) { super(ThreadPool.this, id + "");// 將當前線程加入當前ThreadGroup,並且把線程重命名 } @Override public void run() { while (!interrupted()) { Runnable task = getTask(); if (task == null) return; task.run(); } } } /** * 測試類 * @param args */ public static void main(String[] args) { ThreadPool threadPool = new ThreadPool(3); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 10; i++) { threadPool.execute(createTask(i)); } } public static Runnable createTask(final int id) { return new Runnable() { @Override public void run() { System.out.println("工作線程 " + Thread.currentThread().getName() + " 正在執行Task" + id); } }; } }
這是執行結果
工作線程 0 正在等待任務 工作線程 1 正在等待任務 工作線程 2 正在等待任務 工作線程 0 正在執行Task0 工作線程 1 正在執行Task2 工作線程 2 正在執行Task1 工作線程 2 正在執行Task5 工作線程 2 正在執行Task6 工作線程 2 正在執行Task7 工作線程 2 正在執行Task8 工作線程 2 正在執行Task9 工作線程 2 正在等待任務 工作線程 1 正在執行Task4 工作線程 1 正在等待任務 工作線程 0 正在執行Task3 工作線程 0 正在等待任務
可以看出,線程池在初始化的時候就啓動了3個線程,然後一直處於等待任務的狀態,任務加入後,3個線程併發執行,當所有的任務都完成後,工作線程又處於等待狀態
現在來看看如果我們使用JDK裏面的線程池應該怎麼寫
package com.yellow; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 線程池的模擬 * * @author yellowbaby * */ public class ThreadPool { /** * 測試類 * @param args */ public static void main(String[] args) { ExecutorService threadPool = Executors.newFixedThreadPool(3);//創建一個固定有三個工作線程的線程池 try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 10; i++) { threadPool.execute(createTask(i)); } } public static Runnable createTask(final int id) { return new Runnable() { @Override public void run() { System.out.println("工作線程 " + Thread.currentThread().getName() + " 正在執行Task" + id); } }; } }