寫在前面
??說實話,我個人是不太喜歡寫博客或技術文章的。一來是因為個人水平有限,怕寫出來的東西錯誤百出,誤人子弟;二來在學習的過程中,如果想著寫博客輸出wiki等,會嚴重影響思路(我個人是這么認為的),所以幾次想嘗試寫寫學習筆記啥的都沒能走出第一步。前幾天,因為某些原因給我一些觸動,讓我覺得還是要向某人學習,該輸出的還是要輸出。
??本文主要是我在前段時間學習Java多線程相關知識時,總結的一些個人實操,代碼本機測試OK(PC : MacBook Pro - Mac OS 10.11.6 , JDK : JDK 1.8.0_40),如發現BUG之類,歡迎各位指正。除此之外,說明一下,本文部分代碼和內容參考自《Java并發編程的藝術》與《Java并發編程實戰》,如有雷同,就是引用。
引言
??相信大多數程序員在學習或工作中都聽過或使用過線程池這一簡單高效的并發組件,尤其在一些對響應時間要求非常嚴苛的場景,使用線程池能讓我們高效方便的的寫出并發代碼。但是對于相當一部分人來說,對于線程池的認識也僅僅停留在用過甚至是聽過的階段,并沒有真正的去了解它的工作原理和實現方式。借著最近學習《Java并發編程的藝術》契機,我看了JDK8中幾種線程池的實現和它們對應的特性,再結合書中的案例,實現了一個簡單的線程池。本線程池并沒有過多的考慮各種各樣的應用場景,只是為了在自己學習的基礎上,探究并實踐了線程池實現的原理和機制,雖說代碼不多,但是其用到的思想還是很值得我們在編寫代碼中學習的,個人認為這種:**獲得鎖 → 條件不滿足 → 等待 → 阻塞 → 釋放鎖 → 被通知 → 獲得鎖 → 阻塞返回并執行 → 釋放鎖 **的模式能實現很多并發,異步,消費者生產者的問題。所以代碼不多,思想很妙。下面是示例代碼:
1、線程池接口
??示例代碼:
/**
* Created by luxiaohong on 17/3/7.
*/
public interface IThreadPool<Job extends Runnable> {
int MAX_THREAD_COUNT = 1024; //線程池允許最大的活動線程數
int DEFAULT_THREAD_COUNT = 10; //線程池默認線程數
int MIN_THREAD_COUNT = 1; //線程池最小線程數
void submit(Job job); //提交任務至線程池執行
void shutdown(int count); //關閉一定數量的線程數
void shutdownAll(); //關閉所有數量的線程
} ```
###2、線程池實現類
  示例代碼:
/**
-
Created by luxiaohong on 17/3/7.
*/
public class ThreadPool<Job extends Runnable> implements IThreadPool<Job>{private volatile boolean isShutdown = false; //標識線程池是否被關閉
private int threadCount = DEFAULT_THREAD_COUNT;
private int threadNum = 0; //標識線程的編號
private final LinkedList<Job> jobs = new LinkedList<>(); //用LinkedList來組織待處理任務列表
private final List<Worker> workers = Collections.synchronizedList(new ArrayList<>()); //用List組織工作者線程
public ThreadPool(){
this(DEFAULT_THREAD_COUNT);
}public ThreadPool(int initSize){
if (initSize < MIN_THREAD_COUNT) {
throw new IllegalArgumentException("initSize is negative");
}
if (initSize > MAX_THREAD_COUNT) {
initSize = MAX_THREAD_COUNT;
}
for (int i = 0; i < initSize; i++) { //初始化工作者線程,并啟動線程
Worker worker = new Worker();
Thread t = new Thread(worker, "thread-"+threadNum++);
workers.add(worker);
t.start();}
}
class Worker implements Runnable{
//使用volatile修飾,保證其他線程修改,本線程立即可見
private volatile boolean isAlive = true;
@Override
public void run(){
while (isAlive) { //首先判斷線程是否存活
Job job = null;
synchronized (jobs) { //對任務集合進行加鎖
while (isAlive && jobs.isEmpty()) { //當線程存活且任務隊列為空,本工作線程阻塞等待
try {
jobs.wait(); //阻塞等待
} catch (InterruptedException e) {
//當拋出InterruptedException時,終端標志位會被重置,需要重新設置
Thread.currentThread().interrupt();
return;
}
}
//判斷線程從阻塞返回的條件,如果jobs不為空,則執行
if (!jobs.isEmpty()) {
job = jobs.removeFirst();
}
}
//此處需判斷job是否為空,因為跳出循環可能是由于線程被殺死
if (job != null) {
job.run();
}
}
}
//暫停worker
public void shutdown(){
this.isAlive = false;
}
}
@Override
public void submit(Job job) {
if (job == null) {
throw new NullPointerException("job is null.");
}
if (isShutdown) {
throw new IllegalStateException("threadpool is shutdown.");
}
synchronized (jobs) { //當提交一個任務后,先將任務隊列加鎖在將任務加入隊尾,最后通知工作線程并釋放鎖
jobs.addLast(job);
jobs.notify();
}
}
@Override
public void shutdownAll() {
if (isShutdown) {
throw new IllegalStateException("threadpool is shutdown.");
}
for (Worker worker : workers) {
worker.shutdown();
}
this.isShutdown = true;
}
@Override
public void shutdown(int count) {
synchronized (workers) {
if (count > threadCount) {
throw new IllegalArgumentException("shutdown thread count gt current thread count.");
}
if (count == threadCount) {
//關閉所有線程
shutdownAll();
return;
}
for (int i = threadCount - 1; i > threadCount - count; i--) {
workers.get(i).shutdown();
}
threadCount -= count;
}
}
}
###3、測試線程池主程序
  示例代碼:
/**
- Created by luxiaohong on 17/3/8.
*/
public class ThreadPoolTest {
public static void main(String[] args) {
ThreadPool threadPool = new ThreadPool();
for (int i =0 ; i < 1000 ; i++) {
threadPool.submit(() -> {
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName()+"執行完畢");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
//threadPool.shutdownAll();
}
}
###4、執行結果

###5、總結
  此線程池實際包含兩個重要的組成部分。第一個是任務執行者池(Runnable),第二個是待執行任務池(Runnable)。線程池實現思想就是,當待執行任務池為空即沒有任務時,任務執行者池中的任務執行者(使用Thread包裝的Runable對象)會阻塞在待執行任務池這個條件上,一旦客戶端提交了一個任務,客戶端線程首先對待執行任務池加鎖,然后將任務安全的加入到待執行任務池中,最后通知任務執行者池中的某一個任務執行者,被選中通知的任務執行者將會從待執行任務池中獲取這個任務,然后執行下去。至此,一個任務從提交到執行結束的生命周期就這樣完成了。我們都知道在Java中要想新建一個線程,有兩種方式,一是實現Runnable接口,然后用Thread類包裝,start()調用執行;二是繼承Thread類,重寫run()方法,然后實例化一個對象,start()執行。之前了解到使用實現Runnable接口的方式有很多優點,通過線程池的實現也可以看出,這種方式對于一些復雜任務,更高級的并發組件實現起來,具有很好的解耦性。