個人總結:望對屏幕對面的您有所幫助 一. 線程概述 進程: 有獨立的記憶體控制項和系統資源 應用程式的執行實例 啟動當前電腦任務管理器:taskmgr 進程是程式(任務)的執行過程,它持有資源(共用記憶體,共用文件)和線程。 線程: 進程中執行運算的最小的單位,可完成一個獨立的順序控制流程(執行路徑) C ...
個人總結:望對屏幕對面的您有所幫助
一. 線程概述
進程:
有獨立的記憶體控制項和系統資源 應用程式的執行實例
啟動當前電腦任務管理器:taskmgr
進程是程式(任務)的執行過程,它持有資源(共用記憶體,共用文件)和線程。
線程:
進程中執行運算的最小的單位,可完成一個獨立的順序控制流程(執行路徑)
CPU調度和分派的基本單位
一個線程的生命周期:
· 新建狀態:
使用 new 關鍵字和 Thread 類或其子類建立一個線程對象後,該線程對象就處於新建狀態。它保持這個狀態直到程式 start() 這個線程。
· 就緒狀態:
當線程對象調用了start()方法之後,該線程就進入就緒狀態。就緒狀態的線程處於就緒隊列中,要等待JVM里線程調度器的調度。
· 運行狀態:
如果就緒狀態的線程獲取 CPU 資源,就可以執行 run(),此時線程便處於運行狀態。處於運行狀態的線程最為複雜,它可以變為阻塞狀態、就緒狀態和死亡狀態。
· 阻塞狀態:
如果一個線程執行了sleep(睡眠)、suspend(掛起)等方法,失去所占用資源之後,該線程就從運行狀態進入阻塞狀態。在睡眠時間已到或獲得設備資源後可以重新進入就緒狀態。
· 死亡狀態:
一個運行狀態的線程完成任務或者其他終止條件發生時,該線程就切換到終止狀態。
線程的狀態轉換:
1. 新建狀態(New):新創建了一個線程對象。
2. 就緒狀態(Runnable):線程對象創建後,其他線程調用了該對象的start()方法。該狀態的線程位於可運行線程池中,變得可運行,等待獲取CPU的使用權。
3. 運行狀態(Running):就緒狀態的線程獲取了CPU,執行程式代碼。
4. 阻塞狀態(Blocked):阻塞狀態是線程因為某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,才有機會轉到運行狀態。阻塞的情況分三種:
(一)、等待阻塞:運行的線程執行wait()方法,JVM會把該線程放入等待池中。
(二)、同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程占用,則JVM會把該線程放入鎖池中。
(三)、其他阻塞:運行的線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置為阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。
5. 死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命周期。
多線程:
線程是系統中最小的執行單元;同一進程中可以有多個線程;線程共用進程的資源。
定義:
如果一個進程中 同時運行了多個線程 ,用來完成不同的工作,則稱之為 “多線程”
多個線程交替占用CPU資源,而非真正的並行執行()
優點:
1)充分利用CPU資源
2)簡化編程模型
每個線程式控制制一個指針
3)帶來良好的用戶體驗
創建線程的方法
有兩種:
1. 實現Runnable介面
擁有唯一方法:run()
方法 run 的常規協定是,它可能執行任何所需的動作。
JavaJDK API 給出的解釋:
Runnable 介面應該由那些打算通過某一線程執行其實例的類來實現。類必須定義一個稱為 run 的無參數方法。
設計該介面的目的是為希望在活動時執行代碼的對象提供一個公共協議。例如,Thread 類實現了 Runnable。激活的意思是說某個線程已啟動並且尚未停止。
此外,Runnable 為非 Thread 子類的類提供了一種激活方式。通過實例化某個 Thread 實例並將自身作為運行目標,就可以運行實現 Runnable 的類而無需創建 Thread 的子類。大多數情況下,如果只想重寫 run() 方法,而不重寫其他 Thread 方法,那麼應使用 Runnable 介面。這很重要,因為除非程式員打算修改或增強類的基本行為,否則不應為該類創建子類。
2. 繼承Thread類本身
Thread類:
Java提供了java.lang.Thread類支持多線程編程
1. Thread類常用的構造方法:
序號 |
方法描述 |
1 |
public Thread() 分配新的 Thread 對象。這種構造方法與 Thread(null, null, gname) 具有相同的作用,其中 gname 是一個新生成的名稱。自動生成的名稱的形式為 "Thread-"+n,其中的 n 為整數。 |
2 |
public Thread(Runnable target) 分配新的 Thread 對象。這種構造方法與 Thread(null, target,gname) 具有相同的作用,其中的 gname 是一個新生成的名稱。自動生成的名稱的形式為 “Thread-”+n,其中的 n 為整數。 參數: target - 其 run 方法被調用的對象。 |
3 |
public Thread(Runnable target,String name) 分配新的 Thread 對象。這種構造方法與 Thread(null, target, name) 具有相同的作用。 參數: target - 其 run 方法被調用的對象。 name - 新線程的名稱。 |
4 |
public Thread(String name) 分配新的 Thread 對象。這種構造方法與 Thread(null, null, name) 具有相同的作用。 參數: name - 新線程的名稱。 |
2. Thread類的一些重要方法(對象調用):
序號 |
方法描述 |
1 |
public void start() |
2 |
public void run() |
3 |
public final void setName(String name) |
4 |
public final void setPriority(int priority) |
5 |
public final void setDaemon(boolean on) |
6 |
public final void join(long millisec) |
7 |
public void interrupt() |
8 |
public final boolean isAlive() |
3. Thread類的靜態方法(直接調用):
序號 |
方法描述 |
1 |
public static void yield() |
2 |
public static void sleep(long millisec) |
3 |
public static boolean holdsLock(Object x) |
4 |
public static Thread currentThread() |
5 |
public static void dumpStack() |
二. 主線程
1. 主線程
每一個進程至少只有一個線程
1)main()方法即為主線程入口
2)產生其他子線程的線程
3)必須最後完成執行,因為它執行各種關閉動作
簡單操作:
2. 常見線程名詞解釋
主線程:JVM調用程式main()所產生的線程。
當前線程:這個是容易混淆的概念。一般指通過Thread.currentThread()來獲取的進程。
後臺線程:指為其他線程提供服務的線程,也稱為守護線程。JVM的垃圾回收線程就是一個後臺線程。
前臺線程:是指接受後臺線程服務的線程,其實前臺後臺線程是聯繫在一起,就像傀儡和幕後操縱者一樣的關係。傀儡是前臺線程、幕後操縱者是後臺線程。由前臺線程創建的線程預設也是前臺線程。可以通過isDaemon()和setDaemon()方法來判斷和設置一個線程是否為後臺線程。
三. 繼承Thread 類創建線程
1. 定義一個類繼承Thread類
建議:類名以Thread結尾,(清晰)
public class MyThread extends Thread{
}
2. 重寫run()方法,編寫線程執行體
執行的操作
邏輯處理代碼
3. 創建線程對象,調用start()方法啟動線程
類 對象 = new 類(); 對象.start();//啟動線程 //將會執行步驟二中重寫後的run()方法
當多個線程同時啟動時:
線程間交替執行,順序不定,根據CPU分配的時間片覺得的(不可預測)
每個線程分別執行,幾條不同的執行過程,交替並行執行
為什麼不直接調用run()方法?
如果直接調用run()方法:
執行的線程是main()/主 線程,並且不是並行執行
1. 只有主線程一條執行路徑
2. 依次調用相應次數的run()方法
3. 相當於單線程
4. Run()方法是給底層編譯器用的,程式員只能用start()
執行結構圖:
如果是調用Start()方法:
1)不同子線程獲取到CPU時間片時,執行其線程的相應操作
2)多態執行路徑,主線程和子線程並行交替執行
多個線程同時執行?
1. 多個線程交替執行,不是真正的 “並行”
2. 線程每次執行時長由分配的CPU時間片長度覺得
廣義解釋:
線程中的方法比較有特點,比如:啟動(start),休眠(sleep),停止等,多個線程是交互執行的(cpu在某個時刻。只能執行一個線程,當一個線程休眠了或者執行完畢了,另一個線程才能占用cpu來執行)因為這是cpu的結構來決定的,在某個時刻cpu只能執行一個線程,不過速度相當快,對於人來將可以認為是並行執行的。
四. 實現Runnable 介面創建線程
1. 定義類實現Runnable介面
public class MyRunnable impleents Runnable{
}
2. 實現run()方法,編寫線程執行體
run()方法中編寫線程執行的代碼
3. 創建線程對象,調用start()方法啟動線程
實現Runnable介面創建的線程最終還是要通過將自身實例作為參數傳遞給Thread然後執行
語法:
Thread actress=new Thread(Runnable target ,String name);
例如:
Thread actressThread=new Thread(new Actress(),"Ms.runnable");
actressThread.start();
分析:
使用方式或限制等,與 繼承Thread類 大相徑庭
方法必須實現
1. 創建Runnable實現類對象
2. 將實現類對象交於Thread對象
3. 啟動線程
4. 優點
1.可以避免java的單繼承的特性帶來的局限性;
2.適合多個相同程式的代碼去處理同一個資源情況,把線程同程式的代碼及數據有效的分離,較好的體現了面向對象的設計思想。開發中大多數情況下都使用實現Runnable介面這種方法創建線程。
5. 分析:
電腦CPU處理器在同一時間同一個處理器同一個核只能運行一條線程,當一條線程休眠之後,另外一個線程才獲得處理器時間
三 or 四.比較兩種創建線程的方式
1. 繼承Thread類
1) 編寫簡單,可直接操作線程
2) 適用於但繼承
2. 實現Runnable介面
1)避免但繼承的局限性
2)便於共用資源
3. 結論
(Java只適用於單根繼承)
推薦使用 實現Runnable介面 方式創建線程
五. 線程的狀態和調度
1. 線程的狀態
五個狀態:
創建 就緒 阻塞 運行 死亡
2. 線程調度的方法
線程調度指按照特定機製為多個線程分配CPU的使用權
4. 線程喚醒:
Object類中的notify()方法,喚醒在此對象監視器上等待的單個線程。如果所有線程都在此對象上等待,則會選擇喚醒其中一個線程。選擇是任意性的,併在對實現做出決定時發生。線程通過調用其中一個 wait 方法,在對象的監視器上等待。 直到當前的線程放棄此對象上的鎖定,才能繼續執行被喚醒的線程。被喚醒的線程將以常規方式與在該對象上主動同步的其他所有線程進行競爭;例如,喚醒的線程在作為鎖定此對象的下一個線程方面沒有可靠的特權或劣勢。類似的方法還有一個notifyAll(),喚醒在此對象監視器上等待的所有線程。
註意:Thread中suspend()和resume()兩個方法在JDK1.5中已經廢除。原因:有死鎖傾向。
六. 線程優先順序和線程休眠
1. 調整線程優先順序:
線程的優先順序
線程優先順序由1 - 10表示,1最低,預設優點級為5
優先順序高的線程獲得CPU資源概率較大
調整線程優先順序
setPriority()方法
取值:
線程的優先順序代表哪個線程優先獲取CPU資源
每一個Java線程都有一個優先順序,這樣有助於操作系統確定線程的調度順序。
Java線程的優先順序用整數表示,取值範圍是1~10,Thread類有以下三個靜態常量:
static int MAX_PRIORITY
線程可以具有的最高優先順序,取值為10。
static int MIN_PRIORITY
線程可以具有的最低優先順序,取值為1。
static int NORM_PRIORITY
分配給線程的預設優先順序,取值為5。
補充:
Thread類的setPriority()和getPriority()方法分別用來設置和獲取線程的優先順序。
每個線程都有預設的優先順序。主線程的預設優先順序為Thread.NORM_PRIORITY。
線程的優先順序有繼承關係,比如A線程中創建了B線程,那麼B將和A具有相同的優先順序。
JVM提供了10個線程優先順序,但與常見的操作系統都不能很好的映射。如果希望程式能移植到各個操作系統中,應該僅僅使用Thread類有以下三個靜態常量作為優先順序,這樣能保證同樣的優先順序採用了同樣的調度方式。
具有較高優先順序的線程對程式更重要,並且應該在低優先順序的線程之前分配處理器資源。但是,線程優先順序不能保證線程執行的順序,而且非常依賴於平臺。
2. 線程睡眠:
使用:
讓線程暫時睡眠指定時長,線程進入阻塞狀態
睡眠過後線程會再進入可運行狀態
sleep()方法:
Thread.sleep(long millis)方法,使線程轉到阻塞狀態。millis參數設定睡眠的時間,以毫秒為單位。當睡眠結束後,就轉為就緒(Runnable)狀態。sleep()平臺移植性好。
註:調度sleep()方法需處理InterruptedException異常
3. 線程等待:
Object類中的wait()方法,導致當前的線程等待,直到其他線程調用此對象的 notify() 方法或 notifyAll() 喚醒方法。這個兩個喚醒方法也是Object類中的方法,行為等價於調用 wait(0) 一樣。
七. 線程強制執行
使線程暫停執行,等待其他線程結束後再繼續執行本線程
1. 線程加入:
join()方法,等待其他線程終止。在當前線程中調用另一個線程的join()方法,則當前線程轉入阻塞狀態,直到另一個進程運行結束,當前線程再由阻塞轉為就緒狀態。
註:需處理InterruptedException異常
Mills:以毫秒為單位的等待時長
Nanos:要等待的附迦納秒時長
主線程將被執行,執行調用join()方法的子線程
當此子線程執行完畢後,再返回執行主線程
(其他線程避讓(阻塞),此線程強制加入執行)
八. 線程的禮讓
讓當前線程讓出CPU資源,不再參與資源搶占
暫停當前線程,允許其他具有相同優先順序的線程獲得運行機會(不一定會執行)
改線程處於就緒狀態,不轉為阻塞狀態
只能提供一種可能,但是不能保證一定會實現禮讓
1. 線程讓步:
Thread.yield() 方法,暫停當前正在執行的線程對象,把執行機會讓給相同或者更高優先順序的線程。
九. 線程的不安全問題
當多個線程共用同一資源同時訪問一個數據的時候,一個線程未完成全部操作的時候,其他的線程來修改數據數據,會造成線程不安全問題
爭用條件:
1、當多個線程同時共用訪問同一數據(記憶體區域)時,每個線程都嘗試操作該數據,從而導致數據被破壞(corrupted),這種現象稱為爭用條件
2、原因是,每個線程在操作數據時,會先將數據初值讀【取到自己獲得的記憶體中】,然後在記憶體中進行運算後,重新賦值到數據。
3、爭用條件:線程1在還【未重新將值賦回去時】,線程1阻塞,線程2開始訪問該數據,然後進行了修改,之後被阻塞的線程1再獲得資源,而將之前計算的值覆蓋掉線程2所修改的值,就出現了數據丟失情況。
系統占用CPU資源:隨機性
十. 同步方法和同步代碼塊
線程的同步是為了防止多個線程訪問一個數據對象時,對數據造成的破壞。
同步方法:
使用synchronzed修飾的方法控制對類成員變數的訪問
Synchronized就是為當前的線程聲明一個鎖
一次只允許有一個線程進入執行
語法:
訪問修飾符 synchronized 返回類型 方法名 (參數列表){ ... ... }
or
Synchronized 訪問修飾符 返回類型 方法名 (參數列表){ ... ... }
同步代碼塊:
使用synchronized關鍵字修飾的代碼塊
語法:
Synchronized(syncObject){
//需要同步的代碼塊
}
解析:
1)yncObject為需同步的對象,通常為this
2)效果與同步方法相同
3)避免數據不安全問題
4)一次只允許有一個線程進入
互斥與同步:守恆的能量
1、線程的特點,共用同一進程的資源,同一時刻只能有一個線程占用CPU
2、由於線程有如上的特點,所以就會存在多個線程爭搶資源的現象,就會存在爭用條件這種現象
3、為了讓線程能夠正確的運行,不破壞共用的數據,所以,就產生了同步和互斥的兩種線程運行的機制
4、線程的互斥(加鎖實現):線程的運行隔離開來,互不影響,使用synchronized關鍵字實現互斥行為,此關鍵字即可以出現在方法體之上也可以出現在方法體內,以一種塊的形式出現,在此代碼塊中有線程的等待和喚醒動作,用於支持線程的同步控制
5、線程的同步(線程的等待和喚醒:wait()+notifyAll()):線程的運行有相互的通信控制,運行完一個再正確的運行另一個
6、鎖的概念:比如private final Object lockObj=new Object();
7、互斥實現方式:synchronized關鍵字
synchronized(lockObj){---執行代碼----}加鎖操作
lockObj.wait();線程進入等待狀態,以避免線程持續申請鎖,而不去競爭cpu資源
lockObj.notifyAll();喚醒所有lockObj對象上等待的線程
8、加鎖操作會開銷系統資源,降低效率
## 同步和鎖定
1、鎖的原理
Java中每個對象都有一個內置鎖
當程式運行到非靜態的synchronized同步方法上時,自動獲得與正在執行代碼類的當前實例(this實例)有關的鎖。獲得一個對象的鎖也稱為獲取鎖、鎖定對象、在對象上鎖定或在對象上同步。
當程式運行到synchronized同步方法或代碼塊時才該對象鎖才起作用。
一個對象只有一個鎖。所以,如果一個線程獲得該鎖,就沒有其他線程可以獲得鎖,直到第一個線程釋放(或返回)鎖。這也意味著任何其他線程都不能進入該對象上的synchronized方法或代碼塊,直到該鎖被釋放。
釋放鎖是指持鎖線程退出了synchronized同步方法或代碼塊。
關於鎖和同步,有一下幾個要點:
1)、只能同步方法,而不能同步變數和類;
2)、每個對象只有一個鎖;當提到同步時,應該清楚在什麼上同步?也就是說,在哪個對象上同步?
3)、不必同步類中所有的方法,類可以同時擁有同步和非同步方法。
4)、如果兩個線程要執行一個類中的synchronized方法,並且兩個線程使用相同的實例來調用方法,那麼一次只能有一個線程能夠執行方法,另一個需要等待,直到鎖被釋放。也就是說:如果一個線程在對象上獲得一個鎖,就沒有任何其他線程可以進入(該對象的)類中的任何一個同步方法。
5)、如果線程擁有同步和非同步方法,則非同步方法可以被多個線程自由訪問而不受鎖的限制。
6)、線程睡眠時,它所持的任何鎖都不會釋放。
7)、線程可以獲得多個鎖。比如,在一個對象的同步方法裡面調用另外一個對象的同步方法,則獲取了兩個對象的同步鎖。
8)、同步損害併發性,應該儘可能縮小同步範圍。同步不但可以同步整個方法,還可以同步方法中一部分代碼塊。
9)、在使用同步代碼塊時候,應該指定在哪個對象上同步,也就是說要獲取哪個對象的鎖。
多個併發線程訪問同一資源的同步代碼塊時
1. 同一時刻只能有一個線程進入synchronized (this )同步代碼塊
2. 當一個線程訪問一個synchronized (this) 同步代碼塊時,其他synchronized (this) 同步代碼塊同樣被鎖定
3. 當一個線程訪問一個synchronized (this) 同步代碼塊時,其他線程可以訪問該資源的非synchronized (this) 同步代碼
## 線程同步小結
1、線程同步的目的是為了保護多個線程訪問一個資源時對資源的破壞。
2、線程同步方法是通過鎖來實現,每個對象都有切僅有一個鎖,這個鎖與一個特定的對象關聯,線程一旦獲取了對象鎖,其他訪問該對象的線程就無法再訪問該對象的其他同步方法。
3、對於靜態同步方法,鎖是針對這個類的,鎖對象是該類的Class對象。靜態和非靜態方法的鎖互不幹預。一個線程獲得鎖,當在一個同步方法中訪問另外對象上的同步方法時,會獲取這兩個對象鎖。
4、對於同步,要時刻清醒在哪個對象上同步,這是關鍵。
5、編寫線程安全的類,需要時刻註意對多個線程競爭訪問資源的邏輯和安全做出正確的判斷,對“原子”操作做出分析,並保證原子操作期間別的線程無法訪問競爭資源。
6、當多個線程等待一個對象鎖時,沒有獲取到鎖的線程將發生阻塞。
7、死鎖是線程間相互等待鎖鎖造成的,在實際中發生的概率非常的小。真讓你寫個死鎖程式,不一定好使,呵呵。但是,一旦程式發生死鎖,程式將死掉。
## 深入剖析互斥與同步
互斥的實現(加鎖):synchronized(lockObj); 保證的同一時間,只有一個線程獲得lockObj.
同步的實現:wait()/notify()/notifyAll()
註意:wait()、notify()、notifyAll()方法均屬於Object對象,而不是Thread對象。
void notify()
喚醒在此對象監視器上等待的單個線程。
void notifyAll()
喚醒在此對象監視器上等待的所有線程。
void wait()
導致當前的線程等待,直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法。
當然,wait()還有另外兩個重載方法:
void wait(long timeout)
導致當前的線程等待,直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法,或者超過指定的時間量。
void wait(long timeout, int nanos)
導致當前的線程等待,直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法,或者其他某個線程中斷當前線程,或者已超過某個實際時間量。
同步是兩個線程之間的一種交互的操作(一個線程發出消息另外一個線程響應)
關於等待/通知,要記住的關鍵點是:
必須從同步環境內調用wait()、notify()、notifyAll()方法。線程不能調用對象上等待或通知的方法,除非它擁有那個對象的鎖。
wait()、notify()、notifyAll()都是Object的實例方法。與每個對象具有鎖一樣,每個對象可以有一個線程列表,他們等待來自該信號(通知)。線程通過執行對象上的wait()方法獲得這個等待列表。從那時候起,它不再執行任何其他指令,直到調用對象的notify()方法為止。如果多個線程在同一個對象上等待,則將只選擇一個線程(不保證以何種順序)繼續執行。如果沒有線程等待,則不採取任何特殊操作。
千萬註意:
當在對象上調用wait()方法時,執行該代碼的線程立即放棄它在對象上的鎖。然而調用notify()時,並不意味著這時線程會放棄其鎖。如果線程榮然在完成同步代碼,則線程在移出之前不會放棄鎖。因此,只要調用notify()並不意味著這時該鎖變得可用。
多個線程在等待一個對象鎖時候使用notifyAll():
在多數情況下,最好通知等待某個對象的所有線程。如果這樣做,可以在對象上使用notifyAll()讓所有在此對象上等待的線程衝出等待區,返回到可運行狀態。
### 如何理解同步:Wait Set
Critical Section(臨界資源)Wait Set(等待區域)
wait set 類似於線程的休息室,訪問共用數據的代碼稱為critical section。一個線程獲取鎖,然後進入臨界區,發現某些條件不滿足,然後調用鎖對象上的wait方法,然後線程釋放掉鎖資源,進入鎖對象上的wait set。由於線程釋放釋放了理解資源,其他線程可以獲取所資源,然後執行,完了以後調用notify,通知鎖對象上的等待線程。
Ps:若調用notify();則隨機拿出(這隨機拿出是內部的演算法,無需瞭解)一條在等待的資源進行準備進入Critical Section;若調用notifyAll();則全部取出進行準備進入Critical Section。
十一. 死鎖
個人理解:
多個線程各有自己的鎖,都想拿到自己的鎖,但誰都不想放開自己的鎖
多個線程執行不同的上鎖代碼塊但共用同一資源,雙方都搶不到CPU時間片,就形成了死鎖
資料解釋:
死鎖--兩個線程都在等待對方完成,造成程式的停滯
死鎖的條件:
1)兩個或兩個以上的線程在活動
2)某個線程拿到一個鎖以後,還想拿第二個鎖,造成鎖的嵌套
十二. 生產者和消費者問題
生產者和消費者問題,生產者不斷生成,消費者不斷取走生產者生成的產品
生產者生產出信息之後將其放到一個區域之中,之後消費者從此區域里取出數據
使用Object類中的Wait(線程等待)方法 與 notifyAll(喚醒所有線程)方法
十三. 線程池
優點:
1、線程是稀缺資源,使用線程池可以減少創建和銷毀線程的次數,每個工作線程都可以重覆使用。
2、可以根據系統的承受能力,調整線程池中工作線程的數量,防止因為消耗過多記憶體導致伺服器崩潰。
為什麼使用線程池?
線程缺乏統一管理,占用過多系統資源
缺乏更多功能,如定時執行,定期執行等
使用線程池的好處
1)重用存在的線程,減少對象創建,消亡開銷
2)有效控制最大併發數,提高系統資源使用率
3)定時執行,定期執行
實現原理
(線程比作員工,線程池比作一個團隊,核心池比作團隊中核心團隊員工數,核心池外的比作外包員工)
1. 有了新需求,先看核心員工數量超沒超出最大核心員工數,還有名額的話就新招一個核心員工來做
· 需要獲取全局鎖
2. 核心員工已經最多了,HR 不給批 HC 了,那這個需求只好攢著,放到待完成任務列表吧
3. 如果列表已經堆滿了,核心員工基本沒機會搞完這麼多任務了,那就找個外包吧
· 需要獲取全局鎖
4. 如果核心員工 + 外包員工的數量已經是團隊最多能承受人數了,沒辦法,這個需求接不了了
使用:
線程池所在包:java.util.concurrent
頂級介面是Excutor,(子介面)真正的線程池介面是ExecutorService
Java.util.concurrent.Executors類提供創建線程池的方法
ThreadPoolExcecutor類的使用
(自定義線程池)
構造器中各個參數的含義
。corePoolSize: 核心池的大小
。maximumPoolSize: 線程池最大線程數
keepAliveTime: 表示線程沒有任務執行時最多保持多久時間會終止
unit: 參數keepAliveTime的時間單位
workQueue: 一個阻塞隊列,用來存儲等待執行的任務
threadFactory: 線程工廠,主要用來創建線程
handler: 表示當拒絕處理任務時的策略
· corePoolSize