每一個線程的啓動和結束都是比較消耗時間和佔用資源的。
如果在系統中用到了很多的線程,大量的啓動和結束動作會導致系統的性能變卡,響應變慢。
爲了解決這個問題,引入線程池這種設計思想。
線程池的模式很像生產者消費者模式,消費的對象是一個一個的能夠運行的任務
線程池的思路
線程池的思路和生產者消費者模型是很接近的。
- 準備一個任務容器
- 一次性啓動10個 消費者線程
- 剛開始任務容器是空的,所以線程都wait在上面。
- 直到一個外部線程往這個任務容器中扔了一個“任務”,就會有一個消費者線程被喚醒notify
- 這個消費者線程取出“任務”,並且執行這個任務,執行完畢後,繼續等待下一次任務的到來。
- 如果短時間內,有較多的任務加入,那麼就會有多個線程被喚醒,去執行這些任務。
在整個過程中,都不需要創建新的線程,而是循環使用這些已經存在的線程
開發一個自定義線程池
package multiplethread;
import java.util.LinkedList;
public class ThreadPool {
// 線程池大小
int threadPoolSize;
// 任務容器
LinkedList<Runnable> tasks = new LinkedList<Runnable>();
// 試圖消費任務的線程
public ThreadPool() {
threadPoolSize = 10;
// 啓動10個任務消費者線程
synchronized (tasks) {
for (int i = 0; i < threadPoolSize; i++) {
new TaskConsumeThread("任務消費者線程 " + i).start();
}
}
}
public void add(Runnable r) {
synchronized (tasks) {
tasks.add(r);
// 喚醒等待的任務消費者線程
tasks.notifyAll();
}
}
class TaskConsumeThread extends Thread {
public TaskConsumeThread(String name) {
super(name);
}
Runnable task;
public void run() {
System.out.println("啓動: " + this.getName());
while (true) {
synchronized (tasks) {
while (tasks.isEmpty()) {
try {
tasks.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
task = tasks.removeLast();
// 允許添加任務的線程可以繼續添加任務
tasks.notifyAll();
}
System.out.println(this.getName() + " 獲取到任務,並執行");
task.run();
}
}
}
}
測試線程池
創造一個情景,每個任務執行的時間都是1秒
剛開始是間隔1秒鐘向線程池中添加任務
然後間隔時間越來越短,執行任務的線程還沒有來得及結束,新的任務又來了。就會觀察到線程池裏的其他線程被喚醒來執行這些任務。
package multiplethread;
public class TestThread {
public static void main(String[] args) {
ThreadPool pool= new ThreadPool();
int sleep=1000;
while(true){
pool.add(new Runnable(){
@Override
public void run() {
//System.out.println("執行任務");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
try {
Thread.sleep(sleep);
sleep = sleep>100?sleep-100:sleep;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
使用java自帶線程池
java提供自帶的線程池,而不需要自己去開發一個自定義線程池了。
線程池類ThreadPoolExecutor在包java.util.concurrent下
ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
第一個參數10 表示這個線程池初始化了10個線程在裏面工作
第二個參數15 表示如果10個線程不夠用了,就會自動增加到最多15個線程
第三個參數60 結合第四個參數TimeUnit.SECONDS,表示經過60秒,多出來的線程還沒有接到活兒,就會回收,最後保持池子裏就10個
第四個參數TimeUnit.SECONDS 如上
第五個參數 new LinkedBlockingQueue() 用來放任務的集合
execute方法用於添加新的任務
package multiplethread;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class TestThread {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
//execute方法用於添加新的任務
threadPool.execute(new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("任務1");
}
});
}
}