## 為什么要多線程
為什么要多線程啊?操作系統有多進程管理,那多線程感覺沒有必要。
所謂線程,是程序代碼的執行,一個進程至少得一個線程,不然進程如何執行。
比如說word,如果這個進程沒有多線程,如果有個定時保存文檔的功能,當自動保存的時候,還能繼續輸入文字嗎?當然是不能的。因為單線程只能干一件事,無法并發和并性,直接導致用戶體驗不好。CPU的快速運算能力還有多核就被浪費了。
那么為什么不能用多進程來處理呢?一個進程來接受用戶輸入的文字,另一個進程來自動保存。但是我們需要知道,進程之間是相互隔離的,他們要共享數據,是非常麻煩的。比如說要共享被編輯的文件內容并不容易。
可以說,進程是擁有資源的基本單位,線程是CPU調度的基本單位。
比如有兩個進程,word和QQ。
Word進程打開文件,這是它的資源,QQ打開了Socket,這也是它的資源。
假設word有兩個線程,T1負責用戶文字輸入,T2負責自動保存
QQ也有兩個線程,T3負責從Socket讀取數據,T4負責對音樂數據進行解碼。
操作系統在做調度的時候,基本單位不是word,QQ,而是T1,T2,T3這些線程。

每個線程執行的都是進程代碼中的某個片段。
我們知道Java中總是討論多線程編程,但是從來沒有提過Java多進程,為什么呢?
因為java是一運行在JVM中,對于操作系統來說,JVM就是java.exe運行起來,也就是個進程。
所以說JVM這個進程其實就是他們的容器。
另外Python等動態語言也有虛擬機,也可以進行多線程編程了。
所以說虛擬機是個好東西,程序員不需要再去操作內存,可以屏蔽操作系統的差異,讓寫的程序可以任意在支持該語言虛擬機上的操作系統上運行。
為什么在Java中創建的Thread對象需要調用start方法才能啟動線程,而不是直接調用run呢?如果直接調用run的話,只是用當前線程去執行一個普通的函數,根本沒有新線程創建出來。
所以,想創建一個新的線程出來,必須有準備工作,比如設置好線程的上下文,比如棧(相當于函數調用)、線程狀態,比如線程的PC(Program Counter)等,線程才可以被調度,一旦被調度,就會執行run()方法了。
## 線程池
既然線程是屬于進程的,那么創建一個線程應該很輕松啊,為什么要有線程池這個東西呢?
因為線程雖然輕量,但是對與互聯網應用,每個用戶的請求都創建一個線程,那會非常多。而且眾多的線程去競爭CPU,不斷的切換,也會讓CPU調度不堪重負,很多線程不得不等待。
所以最好的方法用少量的線程,并且讓這些線程盡可能充分利用。也就是說只創建一定數量的線程,讓這些線程來處理所有的任務,任務執行完了以后,線程不結束,而是回到線程池里面,等待接受下一個任務。

這些線程可以預先創建,任務來就不用臨時再創建了,可以立刻開始服務。
但是線程是程序代碼的執行,是個動態的東西,怎么可以預先創建呢?
這是因為線程是有狀態的,當線程池的線程剛剛創建的時候,會讓他們進入阻塞狀態:等待任務的到來。如果任務來了,就喚醒其中的線程,讓它拿到任務去執行。
那如何讓線程進入阻塞狀態呢?
就是一個線程調用take()方法取數據的時候,如果這個Queue中沒有數據,該線程就會阻塞,同樣一個線程調用它的put方法放數據的時候,如果Queue滿了,也會阻塞。

所以說線程池的每個線程的run()方法中,要設置一個循環,每次都嘗試從BlockingQueue獲取任務,如果Queue是空的,就阻塞等待,如果有任務來了,就會通知到線程吃的某個線程去處理,櫥窗完了,試圖從BlockingQueue中獲取任務,依次循環下去。
~~~
線程池中的Worker線程:
public class WorkerThread extends Thread {
private BlockingQueue<Task> taskQueue = null;
private boolean isStopped = false;
//持有一個BlockingQueue的實例
public WorkerThread(BlockingQueue<Task> queue){
taskQueue = queue;
}
public void run(){
while(!isStopped()){
try{
Task task = taskQueue.take();
task.execute();
} catch(Exception e){
//log or otherwise report exception,
//but keep pool thread alive.
}
}
}
......略......
}
~~~
這套代碼已經被吸收到JDK,作為java.util.concurrent包的一部分。