java并發(fā)編程基礎(chǔ)知識
線程概述
進程:是CPU分配資源的最小單元寻咒,是程序的一次動態(tài)執(zhí)行仁锯,它對應(yīng)著從代碼加載锣杂,執(zhí)行至完成的一個完整的過程匙姜,它有自己的生命周期畅厢。它是應(yīng)用程序的執(zhí)行實例,每個進程都是由私有的虛擬地址空間氮昧、代碼框杜、數(shù)據(jù)和其它系統(tǒng)資源組成。進程在運行時創(chuàng)建的資源隨著進程的終止而死亡.
線程:是CPU調(diào)度和指派的基本單元, 是進程中的一個實體袖肥,每個線程都有獨立的生命周期.
線程與進程的關(guān)系:線程是進程內(nèi)的一個執(zhí)行單元咪辱,一個進程可以包含多個線程,進程只提供資源加載空間椎组,其具體資源調(diào)度是由進程中的線程來完成的.
線程五大狀態(tài)
新建狀態(tài)(new)
就緒狀態(tài)(Runnable)
運行狀態(tài)(Running)
阻塞狀態(tài)(Blocked)
死亡狀態(tài)(Dead)
線程創(chuàng)建與啟動
線程創(chuàng)建:
1.繼承Thread類 如: class MyThread extends Thread {...}
2.實現(xiàn)Runnable接口 如: class MyRunnable implements Runnable{...}
啟動:啟動線程使用start()方法
線程之間的協(xié)作
wait/notify/notifyAll
wait:阻塞當前線程油狂,直到 notify 或者 notifyAll 來喚醒????
notify:只能喚醒一個處于 wait 的線程
notifyAll: notifyAll喚醒全部處于 wait 的線程?
notify與notifyAll區(qū)別:notifyAll使所有原來在該對象上等待被notify的線程統(tǒng)統(tǒng)退出wait的狀態(tài),變成等待該對象上的鎖寸癌,一旦該對象被解鎖选调,他們就會去競爭;而notify只是選擇一個wait狀態(tài)線程進行通知灵份,并使它獲得該對象上的鎖,當?shù)谝粋€線程運行完畢以后釋放對象上的鎖,此時如果該對象沒有再次使用notify語句,即便該對象已經(jīng)空閑哮洽,其他wait狀態(tài)等待的線程由于沒有得到該對象的通知填渠,繼續(xù)處在wait狀態(tài),直到這個對象發(fā)出一個notify或notifyAll.
sleep/yield/join
sleep:讓當前線程暫停指定時間,只是讓出CPU的使用權(quán)氛什,并不釋放鎖
yield:暫停當前線程的執(zhí)行莺葫,讓出CPU的使用權(quán),讓其他線程有機會執(zhí)行枪眉,不能指定時間捺檬。會讓當前線程從運行狀態(tài)轉(zhuǎn)變?yōu)榫途w狀態(tài), 一般情況使用比較少.
join:等待調(diào)用join方法的線程執(zhí)行結(jié)束,才執(zhí)行后面的代碼其調(diào)用一定要在 start 方法之后贸铜,使用場景: 當父線程需要等待子線程執(zhí)行結(jié)束才執(zhí)行后面內(nèi)容或者需要某個子線程的執(zhí)行結(jié)果會用到j(luò)oin方法?
線程優(yōu)先級
每個線程執(zhí)行時都有一個優(yōu)先級的屬性堡纬,優(yōu)先級高的線程可以獲得較多的執(zhí)行機會,而優(yōu)先級低的線程則獲得較少的執(zhí)行機會蒿秦。與線程休眠類似烤镐,線程的優(yōu)先級仍然無法保障線程的執(zhí)行次序。只不過棍鳖,優(yōu)先級高的線程獲取CPU資源的概率較大炮叶,優(yōu)先級低的也并非沒機會執(zhí)行。
每個線程默認的優(yōu)先級都與創(chuàng)建它的父線程具有相同的優(yōu)先級渡处,在默認情況下镜悉,main線程具有普通優(yōu)先級。
Thread類提供了setPriority(int newPriority)和getPriority()方法來設(shè)置和返回一個指定線程的優(yōu)先級医瘫。
默認常量 MAX_PRIORITY? =10? ? MIN_PRIORITY? =1? NORM_PRIORITY? =5
并發(fā)線程三要素
原子性 : 即一個不可再分割的顆粒,在java中原子性一般指一個或者多個操作要么全部執(zhí)行成功侣肄,要么全部執(zhí)行失敗
可見性: 當多線程訪問同一個變量時,如果其中某一個線程對其做了修改登下,其它線程能立即獲取到最新的值
有序性:程序執(zhí)行的順序按照代碼的先后順序執(zhí)行(處理器可能對內(nèi)部指令重新排序)
鎖
鎖描述
在java1.5之前都是使用synchronized關(guān)鍵字保證同步的茫孔,通過使用一致的鎖定協(xié)議來協(xié)調(diào)對共享狀態(tài)的訪問,都采用獨占的方式來訪問這些變量被芳,即一個線程只有拿到該共享變量的鎖缰贝,才可以訪問該變量,對共享變量進行操作。
悲觀鎖
描述:每次操作都會加鎖,會造成線程阻塞(Synchronized實現(xiàn))畔濒。
悲觀鎖缺點:
1. 悲觀鎖在多線程的競爭下剩晴,加鎖和釋放鎖會導致比較多的上下文切換和調(diào)度延遲從而引發(fā)性能問題,
2. 一個線程持有鎖會導致其它需要此鎖的線程掛起侵状,如果一個優(yōu)先級高的線程調(diào)用一個優(yōu)先級低的線程線程釋放的鎖會導致優(yōu)先級倒置赞弥,引發(fā)性能問題
CAS樂觀鎖
描述:每次操作不加鎖而是假設(shè)沒有沖突而去完成某項操作,如果因為沖突失敗而重試趣兄,直到成功為止,不會造成線程阻塞绽左。(CAS實現(xiàn))
CAS樂觀鎖:當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值艇潭,而其它線程都失敗拼窥,失敗的線程并不會被掛起戏蔑,而是被告知這次競爭中失敗,并可以再次嘗試鲁纠。
CAS原理:CAS操作包含3個數(shù)值总棵,需要讀寫內(nèi)存的地址(V)、進行比較的預期原值(A)和擬寫入的新值(B)改含。如果內(nèi)存位置V的值與預期原值A(chǔ)相匹配情龄,那么處理器會自動將該位置值更新為新值B。否則處理器不做任何操作捍壤。無論哪種情況骤视,它都會在 CAS 指令之前返回該位置的值。
CAS缺點: 1.ABA問題 (通過AtomicStampedReference類解決,即通過該類的compareAndSet方法首先檢查當前引用是否等于預期引用白群,并且當前標志是否等于預期標志尚胞,如果全部相等,則以原子方式將該引用和該標志的值設(shè)置為給定的更新值)帜慢;2.自旋CAS導致CPU消耗非常大笼裳;
CAS與Synchronized使用場景
1.對于資源競爭較少(線程沖突較輕)的情況,使用synchronized同步鎖進行線程阻塞和喚醒切換以及用戶態(tài)內(nèi)核態(tài)間的切換操作額外浪費消耗cpu資源粱玲;而CAS基于硬件實現(xiàn),不需要進入內(nèi)核,不需要切換線程,操作自旋幾率較少躬柬,因此可以獲得更高的性能。
2.對于資源競爭嚴重(線程沖突嚴重)的情況抽减,CAS自旋的概率會比較大允青,從而浪費更多的CPU資源,效率低于synchronized颠锉。CAS在判斷兩次讀取的值不一樣的時候會放棄操作,但為了保證結(jié)果正確史汗,通常都會繼續(xù)嘗試循環(huán)再次發(fā)起CAS操作琼掠。
public final int getAndIncrement() {
? ? ? ? for (;;) {
? ? ? ? ? ? int current = get();
? ? ? ? ? ? int next = current + 1;
? ? ? ? ? ? if (compareAndSet(current, next))
? ? ? ? ? ? ? ? return current;
? ? ? ? }
}
如果compareAndSet(current, next)方法成功執(zhí)行,則直接返回停撞;如果線程競爭激烈瓷蛙,導致compareAndSet(current, next)方法一直不能成功執(zhí)行,則會一直循環(huán)等待戈毒,直到耗盡cpu分配給該線程的時間片艰猬,從而大幅降低效率。
公平鎖與非公平鎖
公平鎖是指多個線程按照申請鎖的順序來獲取鎖埋市,ReentrantLock提供了公平鎖與非公平鎖實現(xiàn)
描述: 如果線程A獲取鎖冠桃,這時候B線程請求持有該鎖時,會被掛起道宅,這時如果有線程C也來競爭該鎖腊满,那么如果是使用公平鎖的情況下套么,B線程獲取該鎖,如果是非公平鎖場景下碳蛋,B線程與C線程互相競爭獲取A線程釋放的鎖
公平鎖: ReentrantLock rLock = new ReentrantLock(true);
非公平鎖: ReentrantLock rLock = new ReentrantLock(false);? 默認為非公平鎖
使用場景: ReentrantLock公平鎖會帶來性能開銷,如果沒有公平性場景下建議盡量不要使用公平鎖,同時ReentrantLock也是個一個獨占鎖省咨,同一時刻只允許一個線程獲取鎖的場景下可使用該鎖(如:消息生產(chǎn)-消費模型)
自旋鎖(Spin Lock)
自旋鎖即如果持有鎖的線程可以在短時間內(nèi)釋放鎖資源肃弟,那么等待競爭鎖的那些線程不需要在內(nèi)核狀態(tài)和用戶狀態(tài)之間進行切換。 它只需要等待零蓉,并且鎖可以在釋放鎖之后立即獲得鎖笤受。這樣好處是可以避免消耗用戶線程和內(nèi)核切換。
使用場景:
自旋鎖是一種耗費CPU資源的鎖敌蜂。 如果自旋鎖的對象一直無法獲得臨界資源箩兽,則線程也無法在沒有執(zhí)行實際計算的情況下一直進行這樣就會導致CPU空轉(zhuǎn),因此需要設(shè)置自旋鎖的最大等待時間章喉。如果持有鎖的線程在旋轉(zhuǎn)等待的最大時間沒有釋放鎖汗贫,則自旋鎖線程將停止旋轉(zhuǎn)進入阻塞狀態(tài)。
JDK1.6開啟自旋鎖 -XX:+UseSpinning秸脱,1.7之后控制器收回到JVM自主控制
偏向鎖(Biased Lock)/輕量級鎖(Lightweight Lock)/重量級鎖(Heavyweight Lock)
Java 5引入的一種狀態(tài)鎖機制落包,通過鎖升級來實現(xiàn)高效的synchronized。
其原理是通過對象監(jiān)視器中的狀態(tài)屬性來標示鎖的狀態(tài)摊唇,然后一級一級的旋轉(zhuǎn)對鎖進行升級咐蝇。
偏向鎖:偏向鎖是指一段同步代碼一直被一個線程所訪問,不存在線程競爭的情況下巷查,那么該線程會自動獲得一個偏向鎖有序。這樣可以降低獲取鎖的代價。如果在運行過程中岛请,遇到了其他線程搶占鎖旭寿,則持有偏向鎖的線程會被掛起,JVM會消除它身上的偏向鎖髓需,將鎖恢復到標準的輕量級鎖许师。
輕量級鎖:輕量級鎖是指當鎖是偏向鎖的時候,被另一個線程所訪問僚匆,偏向鎖就會升級為輕量級鎖微渠,其他線程會通過自旋的形式嘗試獲取鎖,不會阻塞咧擂,提高性能逞盆。
重量級鎖:重量級鎖是指當鎖為輕量級鎖的時候,另一個線程雖然是自旋松申,但自旋不會一直持續(xù)下去云芦,當自旋一定次數(shù)的時候俯逾,還沒有獲取到鎖,就會進入阻塞舅逸,該鎖膨脹為重量級鎖桌肴。重量級鎖會讓其他申請的線程進入阻塞,性能降低琉历。
可重入鎖
可重入鎖是指在同一個線程在外層方法獲取鎖的時候坠七,在進入內(nèi)層方法會自動獲取鎖。RenntrantLock/sunchronized在某種意義上可以任務(wù)是一種可重入鎖旗笔,重入鎖可以一定程度避免死鎖
//當線程A調(diào)用方法methodA時彪置,會自動獲取methodB的鎖,從而避免死鎖
synchronized void methodA(){
? ? methodB();
}
synchronized void methodB(){
}
讀寫鎖
ReentrantReadWriteLock提供了讀寫鎖實現(xiàn)蝇恶,一般適用于讀多寫少的場景
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
Lock readLock = readWriteLock.readLock();
Lock writeLock = readWriteLock.writeLock();
同步控制
RenntrantLock
ReadWriteLock
Condition
Semaphore
CountDownLatch
CyclicBarrier
LockSupport
線程死鎖
描述:死鎖是指兩個或者兩個以上的線程在執(zhí)行過程中拳魁,因爭奪資源中造成的互相等待的現(xiàn)象,由于線程被無限期地阻塞,因此程序不能正常運行撮弧。
死鎖產(chǎn)生條件
互斥條件:
線程對已經(jīng)獲取的資源進行排它性使用潘懊,即該資源同時只由一個線程占用,如果此時還有其他線程請求獲取該資源,該請求只能等待直到占有改資源的線程釋放該資源
請求并持有條件:
一個線程已經(jīng)持有了一個資源想虎,但又提出了新的資源請求卦尊,而新資源已被其他線程占有,所以當前線程會被阻塞舌厨,阻塞的同時并不獲取自己已獲取的資源.
不可剝奪條件:
線程獲取到資源在自己使用完成未釋放前其它線程不能搶占該資源岂却,只有自己釋放后才能有CPU自由調(diào)度分配.
循環(huán)等待條件:
若干線程之間形成一種頭尾相接的循環(huán)等待資源情況,即線程A—>線程B—> 線程C —> 線程D —> 線程A
死鎖避免與處理
線程死鎖必出滿足上述四個必要條件,只要其中一個被破壞就不會產(chǎn)生死鎖裙椭,所以躏哩,在系統(tǒng)設(shè)計、進程調(diào)度等方面應(yīng)注意避免讓這四個必要條件成立揉燃,在確定資源的合理分配算法時扫尺,避免進程永久占據(jù)系統(tǒng)資源才能最大可能地避免、預防和解除死鎖炊汤。此外正驻,也要防止進程在處于等待狀態(tài)的情況下占用資源。因此抢腐,對資源的分配要給予合理的規(guī)劃姑曙。
由于資源競爭受CPU控制,所以一般我們在請求并持有條件和循環(huán)等待這兩個階段對線程死鎖做處理.
關(guān)鍵字
volatile關(guān)鍵字
定義:java編程語言允許線程訪問共享變量迈倍,為了確保共享變量能被準確和一致地更新伤靠,java語言提供了volatile關(guān)鍵字,它能保證所有線程對該變量訪問的可見性.
原理: 使用volatile修改的共享變量會將當前處理器緩存行的數(shù)據(jù)寫回到內(nèi)存,同時會使在其他cpu里緩存了該內(nèi)存地址的數(shù)據(jù)無效.
作用:保證共享變量訪問的可見性,但不保證原子性
常用場景: 對一個變量的寫操作先行發(fā)生于后面對這個變量的讀操作 即1)對變量的寫操作不依賴于當前值 2)該變量沒有包含在具有其他變量的不變式中
synchronized關(guān)鍵字
定義:在java中每個對象都擁有一個鎖標記啼染,也稱為監(jiān)視器宴合,多線程同時訪問某個對象時焕梅,線程只有獲得該對象的鎖才能訪問,java可以使用synchronized關(guān)鍵字來標記一個方法或者代碼塊卦洽,當某個線程調(diào)用該對象的synchronized方法或者訪問synchronized代碼塊時贞言,這個線程便獲得了該對象的鎖,其他線程暫時無法訪問這個方法阀蒂,只有等待這個方法執(zhí)行完畢或者代碼塊執(zhí)行完畢蜗字,這個線程才會釋放該對象的鎖,其他線程才能執(zhí)行這個方法或者代碼塊.
原理: sychronized分為同步代碼塊和同步方法兩類,原理有部分區(qū)別
? ? ? 1)同步代碼塊是使用MonitorEnter和MoniterExit指令實現(xiàn)的,在編譯時,MonitorEnter指令被插入到同步代碼塊的開始位置,MoniterExit指令被插入到同步代碼塊的結(jié)束位置和異常位置脂新。任何對象都有一個Monitor與之關(guān)聯(lián),當Monitor被持有后將處于鎖定狀態(tài)粗梭。MonitorEnter指令會嘗試獲取Monitor的持有權(quán)争便,即嘗試獲取鎖。
? ? ? 2)同步方法依賴flags標志ACC_SYNCHRONIZED實現(xiàn)断医,ACC_SYNCHRONIZED標志表示方法為同步方法滞乙,如果為非靜態(tài)方法(沒有ACC_STATIC標志),使用調(diào)用該方法的對象作為鎖對象;如果為靜態(tài)方法(有ACC_STATIC標志),使用該方法所屬的Class類在JVM的內(nèi)部對象表示class作為鎖對象鉴嗤。
作用: synchronized關(guān)鍵字保障方法或者代碼塊在運行時斩启,同一時刻只有一個方法可以進入臨界區(qū),同時它還可以保證共享變量的內(nèi)存可見性
常用場景:1.修飾實例方法,作用于當前實例加鎖醉锅,進入同步代碼前要獲得當前實例的鎖;
2.修飾靜態(tài)方法兔簇,作用于當前類對象加鎖,進入同步代碼前要獲得當前類對象的鎖;
3.修飾代碼塊硬耍,指定加鎖對象垄琐,對給定對象加鎖,進入同步代碼庫前要獲得給定對象的鎖.
線程池Thread Pool
線程池概述:線程池是指在初始化一個多線程應(yīng)用程序過程中創(chuàng)建一個線程集合经柴,然后在需要執(zhí)行新的任務(wù)時重用這些線程而不是新建一個線程狸窘。線程池的容量通常完全取決于可用內(nèi)存數(shù)量和應(yīng)用程序的需求。線程池中的每個線程都有被分配一個任務(wù)坯认,一旦任務(wù)已經(jīng)完成了翻擒,線程回到池子中并等待下一次分配任務(wù)。使用線程池可以降低資源消耗牛哺。通過重復利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗; 同時可以提高響應(yīng)速度,當任務(wù)到達時,任務(wù)可以不需要的等到線程創(chuàng)建就能立即執(zhí)行; 還有提高線程的可管理性,線程是稀缺資源,如果無限制的創(chuàng)建,不僅會消耗系統(tǒng)資源,還會降低系統(tǒng)的穩(wěn)定性,使用線程池可以進行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控陋气。可以通過ThreadPoolExecutor創(chuàng)建線程池荆隘。
線程池生命周期
RUNNING:接收新任務(wù),并且處理任務(wù)隊列中的任務(wù).
SHUTDOWN:不接收新任務(wù),但是處理任務(wù)隊列的任務(wù).
STOP:不接收新任務(wù),不處理任務(wù)隊列,同時中斷所有進行中的任務(wù).
TIDYING:所有任務(wù)已經(jīng)被終止,工作線程數(shù)量為 0,到達該狀態(tài)會執(zhí)行terminated().
TERMINATED: terminated()執(zhí)行完畢.
核心參數(shù)
corePoolSize:最小存活的工作線程數(shù)量恩伺,當提交一個任務(wù)到線程池時,線程池會創(chuàng)建一個線程來執(zhí)行任務(wù)椰拒,即使其他空閑的基本線程能夠執(zhí)行新任務(wù)也會創(chuàng)建線程晶渠,等到需要執(zhí)行的任務(wù)數(shù)大于線程池基本大小時就不再創(chuàng)建凰荚。如果調(diào)用了線程池的prestartAllCoreThreads方法,線程池會提前創(chuàng)建并啟動所有基本線程褒脯。
maximumPoolSize:最大的線程數(shù)量便瑟,線程池允許創(chuàng)建的最大線程數(shù)。如果隊列滿了番川,并且已創(chuàng)建的線程數(shù)小于最大線程數(shù)到涂,則線程池會再創(chuàng)建新的線程執(zhí)行任務(wù)。值得注意的是如果使用了無界的任務(wù)隊列這個參數(shù)就沒什么效果颁督。
keepAliveTime:線程活動保持時間践啄,線程池的工作線程空閑后,保持存活的時間沉御。所以如果任務(wù)很多屿讽,并且每個任務(wù)執(zhí)行的時間比較短,可以調(diào)大這個時間吠裆,提高線程的利用率伐谈,時間單位由TimeUnit指定
workQueue:工作隊列,存儲待執(zhí)行的任務(wù)
直接提交隊列: synchronousQueue,又稱呼為無緩沖等待隊列试疙,它不會保存提交的任務(wù)诵棵,而是將任務(wù)直接提交給線程。如果不存在可用于立即運行任務(wù)的線程祝旷,則試圖把任務(wù)加入隊列將失敗履澳,因此會構(gòu)造一個新的線程。此策略可以避免在處理可能具有內(nèi)部依賴性的請求集合時出現(xiàn)鎖定缓屠。直接提交通常要求無界即 maximumPoolSizes 設(shè)定成Integer.MAX_VALUE以避免拒絕新提交的任務(wù)奇昙。當命令以超過隊列所能處理的平均數(shù)連續(xù)到達時,此策略允許無界線程具有增長的可能性.
有界任務(wù)隊列:ArrayBlockingQueue,基于數(shù)組的先進先出隊列敌完,此隊列創(chuàng)建時必須指定大小.如果沒有達到corePoolSize的值储耐,則新建線程(核心線程)執(zhí)行任務(wù),如果達到了滨溉,則入隊等候什湘,如果隊列已滿,則新建線程(非核心線程)執(zhí)行任務(wù)晦攒。
無界任務(wù)隊列: LinkedBlockingQueue,基于鏈表的先進先出隊列闽撤,如果創(chuàng)建時沒有指定此隊列大小,則默認為Integer.MAX_VALUE.如果當前線程數(shù)小于核心線程數(shù)脯颜,則新建線程(核心線程)處理任務(wù)哟旗;如果當前線程數(shù)等于核心線程數(shù),則進入隊列等待。
DelayQueue中的元素只有當其指定的延遲時間到了闸餐,才能夠從隊列中獲取到該元素饱亮。DelayQueue是一個沒有大小限制的隊列,因此往隊列中插入數(shù)據(jù)的操作(生產(chǎn)者)永遠不會被阻塞舍沙,而只有獲取數(shù)據(jù)的操作(消費者)才會被阻塞.
使用場景:DelayQueue使用場景較少近上,但都相當巧妙,常見的例子比如使用一個DelayQueue來管理一個超時未響應(yīng)的連接隊列.
PriorityBlockingQueue: 基于優(yōu)先級的阻塞隊列(優(yōu)先級的判斷通過構(gòu)造函數(shù)傳入的Compator對象來決定),但需要注意的是PriorityBlockingQueue并不會阻塞數(shù)據(jù)生產(chǎn)者拂铡,而只會在沒有可消費的數(shù)據(jù)時壹无,阻塞數(shù)據(jù)的消費者。因此使用的時候要特別注意感帅,生產(chǎn)者生產(chǎn)數(shù)據(jù)的速度絕對不能快于消費者消費數(shù)據(jù)的速度芒篷,否則時間一長都许,會最終耗盡所有的可用堆內(nèi)存空間.
RejectExecutionHandler:拒絕策略蘸鲸,線程池滿后會觸發(fā)
AbortPolicy:默認策略,終止任務(wù),拋出RejectedException
CallerRunsPolicy:在調(diào)用者線程執(zhí)行當前任務(wù)孵淘,不拋異常(弊端:任務(wù)提交線程的性能極有可能會在某些業(yè)務(wù)場景下急劇下降)
DiscardPolicy: 拋棄策略畔师,直接丟棄任務(wù)往声,不拋異常
DiscardOldersPolicy:拋棄最老的任務(wù)柠座,執(zhí)行當前任務(wù)铅祸,不拋異常
線程池四種模型
CachedThreadPool模型: 一個可緩存的線程池作箍,如果線程池的當前規(guī)模超過了處理需求時硬梁,那么將回收空閑的線程,當需求增加時胞得,則可以添加新的線程荧止,線程池的規(guī)模不存在任何的限制.
示例: ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
源碼:
public static ExecutorService newCachedThreadPool() {
? ? return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
? ? ? ? ? ? 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}
FixedThreadPool模型: 一個固定大小的線程池,提交一個任務(wù)時就創(chuàng)建一個線程阶剑,直到達到線程池的最大數(shù)量跃巡,這時線程池的大小將不再變化.
示例:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads);
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads, ThreadFactory threadFactory);
源碼:
public static ExecutorService newFixedThreadPool(int nThreads) {
? ? return new ThreadPoolExecutor(nThreads, nThreads,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0L, TimeUnit.MILLISECONDS,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? new LinkedBlockingQueue<Runnable>());
}
SingleThreadPool: 一個單線程的線程池,它只有一個工作線程來執(zhí)行任務(wù)牧愁,可以確保按照任務(wù)在隊列中的順序來串行執(zhí)行素邪,如果這個線程異常結(jié)束將創(chuàng)建一個新的線程來執(zhí)行任務(wù).
示例:
ExecutorService singleThreadPool = Executors.newSingleThreadPool();
源碼:
public static ExecutorService newSingleThreadExecutor() {
? ? return new FinalizableDelegatedExecutorService
? ? ? ? (new ThreadPoolExecutor(1, 1,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0L, TimeUnit.MILLISECONDS,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? new LinkedBlockingQueue<Runnable>()));
}
ScheduledThreadPool:一個固定大小的線程池,并且以延遲或者定時的方式來執(zhí)行任務(wù)猪半,類似于Timer(推薦).
示例:
ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(int corePoolSize);
源碼:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
? ? return new ScheduledThreadPoolExecutor(corePoolSize);
}
//ScheduledThreadPoolExecutor():
public ScheduledThreadPoolExecutor(int corePoolSize) {
? ? super(corePoolSize, Integer.MAX_VALUE,
? ? ? ? ? DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
? ? ? ? ? new DelayedWorkQueue());
}
使用規(guī)范與場景
使用規(guī)范:
線程池不允許使用Executors去創(chuàng)建兔朦,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規(guī)則磨确,規(guī)避資源耗盡的風險沽甥。
說明:Executors各個方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor: 主要問題是堆積的請求處理隊列可能會耗費非常大的內(nèi)存,甚至OOM乏奥。
2)newCachedThreadPool和newScheduledThreadPool: 主要問題是線程數(shù)最大數(shù)是Integer.MAX_VALUE摆舟,可能會創(chuàng)建數(shù)量非常多的線程,甚至OOM。
手動創(chuàng)建線程池注意點:
1) 任務(wù)獨立恨诱。如何任務(wù)依賴于其他任務(wù)媳瞪,那么可能產(chǎn)生死鎖。例如某個任務(wù)等待另一個任務(wù)的返回值或執(zhí)行結(jié)果胡野,那么除非線程池足夠大材失,否則將發(fā)生線程饑餓死鎖。
2) 合理配置阻塞時間過長的任務(wù)硫豆。如果任務(wù)阻塞時間過長龙巨,那么即使不出現(xiàn)死鎖,線程池的性能也會變得很糟糕熊响。在Java并發(fā)包里可阻塞方法都同時定義了限時方式和不限時方式旨别。例如Thread.join,BlockingQueue.put,CountDownLatch.await等,如果任務(wù)超時汗茄,則標識任務(wù)失敗秸弛,然后中止任務(wù)或者將任務(wù)放回隊列以便隨后執(zhí)行,這樣洪碳,無論任務(wù)的最終結(jié)果是否成功递览,這種辦法都能夠保證任務(wù)總能繼續(xù)執(zhí)行下去。
3)設(shè)置合理的線程池大小瞳腌。多線程應(yīng)用并非線程越多越好绞铃,需要根據(jù)系統(tǒng)運行的軟硬件環(huán)境以及應(yīng)用本身的特點決定線程池的大小,一般來說嫂侍,如果代碼結(jié)構(gòu)合理的話儿捧,線程數(shù)目與CPU 數(shù)量相適合即可。如果線程運行時可能出現(xiàn)阻塞現(xiàn)象挑宠,可相應(yīng)增加池的大蟹贫堋;如有必要可采用自適應(yīng)算法來動態(tài)調(diào)整線程池的大小各淀,以提高CPU 的有效利用率和系統(tǒng)的整體性能懒鉴。 公式線程池大小=NCPU *UCPU(1+W/C)。
4) 選擇合適的阻塞隊列碎浇。newFixedThreadPool和newSingleThreadExecutor都使用了無界的阻塞隊列疗我,無界阻塞隊列會有消耗很大的內(nèi)存,如果使用了有界阻塞隊列南捂,它會規(guī)避內(nèi)存占用過大的問題吴裤,但是當任務(wù)填滿有界阻塞隊列,新的任務(wù)該怎么辦溺健?在使用有界隊列是麦牺,需要選擇合適的拒絕策略钮蛛,隊列的大小和線程池的大小必須一起調(diào)節(jié)。對于非常大的或者無界的線程池剖膳,可以使用SynchronousQueue來避免任務(wù)排隊魏颓,以直接將任務(wù)從生產(chǎn)者提交到工作者線程。
主要場景:
高并發(fā)吱晒、任務(wù)執(zhí)行時間短的業(yè)務(wù)怎樣使用線程池甸饱?并發(fā)不高、任務(wù)執(zhí)行時間長的業(yè)務(wù)怎樣使用線程池仑濒?并發(fā)高叹话、業(yè)務(wù)執(zhí)行時間長的業(yè)務(wù)怎樣使用線程池?
1.高并發(fā)墩瞳、任務(wù)執(zhí)行時間短的業(yè)務(wù)驼壶,線程池線程數(shù)可以設(shè)置為CPU核數(shù)+1,減少線程上下文的切換
2.并發(fā)不高喉酌、任務(wù)執(zhí)行時間長的業(yè)務(wù)要區(qū)分開看:
? a)假如是業(yè)務(wù)時間長集中在IO操作上热凹,也就是IO密集型的任務(wù),因為IO操作并不占用CPU泪电,所以不要讓所有的CPU閑下來般妙,可以加大線程池中的線程數(shù)目,讓CPU處理更多的業(yè)務(wù)
? b)假如是業(yè)務(wù)時間長集中在計算操作上相速,也就是計算密集型任務(wù)股冗,這個就沒辦法了,可以和(1)一樣和蚪,線程池中的線程數(shù)設(shè)置得少一些,減少線程上下文的切換
3.并發(fā)高烹棉、業(yè)務(wù)執(zhí)行時間長攒霹,解決這種類型任務(wù)的關(guān)鍵不在于線程池而在于整體架構(gòu)的設(shè)計,看看這些業(yè)務(wù)里面某些數(shù)據(jù)是否能做緩存是第一步浆洗,增加服務(wù)器是第二步催束,至于線程池的設(shè)置,設(shè)置參考(2)伏社。最后抠刺,業(yè)務(wù)執(zhí)行時間長的問題,也可能需要分析一下摘昌,看看能不能使用中間件對任務(wù)進行拆分和解耦
線程池執(zhí)行過程
線程安全容器
Vector/HashTable/StringBuffer
通過synchronized給方法加內(nèi)置鎖來實現(xiàn)線程安全
原子類 AtomicXXX
ConcurrentHashMap
BlockingQueue/BlockingDeque
CopyOnWriteArrayList/CopyOnWriteArraySet
ThreadLocal