摘要:對象改變條件對象當(dāng)前線程要等待線程終止之后才能從返回。如果線程在上的操作中被中斷,通道會被關(guān)閉,線程的中斷狀態(tài)會被設(shè)置,并得到一個(gè)。清除線程的中斷狀態(tài)。非公平性鎖雖然可能造成饑餓,但極少的線程切換,保證其更大的吞吐量。
聲明:Java并發(fā)的內(nèi)容是自己閱讀《Java并發(fā)編程實(shí)戰(zhàn)》和《Java并發(fā)編程的藝術(shù)》整理來的。
個(gè)人網(wǎng)站
圖文并茂請戳
思維導(dǎo)圖下載請戳
目錄
(1)基礎(chǔ)概念
(2)線程
(3)鎖
(4)同步器
(5)并發(fā)容器和框架
(6)Java并發(fā)工具類
(7)原子操作類
(8)Executor框架(執(zhí)行機(jī)制)
(9)其他
(一).基礎(chǔ)概念
1.可見性和原子性
可見性:一個(gè)線程修改了共享變量的值,另一個(gè)線程可以讀到這個(gè)修改的值。
原子性:不可被中斷的一個(gè)或一系列操作。
如何保障原子性:
使用總線鎖保證原子性。
使用緩存鎖保證原子性。
2.原子操作的三種實(shí)現(xiàn):
(1).CAS(Compare And Swap 比較與交換)
需要輸入兩個(gè)數(shù)值(一個(gè)舊值和一個(gè)新值),在操作期間先比較舊值有沒有發(fā)生變化,如果沒有發(fā)生變化,才交換成新值,如果發(fā)生了變化就不交換。
存在的三大問題:
ABA問題:如果一個(gè)值原來是A,變成了B,又變成了A,那么使用CAS進(jìn)行檢查時(shí)會發(fā)現(xiàn)它的值沒有發(fā)生變化,但是實(shí)際是發(fā)生了變化。解決方案:1.使用版本號,在變量前面追加版本號,每次變量更新都把版本號加1。JDK提供的類:AtomicStampedReference。
循環(huán)時(shí)間長開銷大。
只能保證一個(gè)共享變量的原子操作。
解決方案:JDK提供AtomicReference類來保證引用對象之間的原子性,可以把多個(gè)變量放在一個(gè)對象里進(jìn)行CAS操作。
(2).鎖
(3).JDK并發(fā)包的支持
如:AtomicBoolean(用原子方式更新的boolean值),
AtomicInteger(用原子方式更新的int值),
AutomicLong(用原子方式更新的long值)。
3.同步原語
(1).volatile
特點(diǎn):
可見性:對一個(gè)volatile變量的讀,總是能看到任意線程對這個(gè)volatile變量最后的寫入。
原子性:對任意單個(gè)volatile變量的讀/寫具有原子性。
從內(nèi)存語義角度:volatile的寫-讀與鎖的釋放-獲取有相同的內(nèi)存效果。
為了實(shí)現(xiàn)volatile的內(nèi)存語義,編譯期在生成字節(jié)碼時(shí),會在指令序列中插入內(nèi)存屏障來禁止特定類型的處理器重排序。
從編譯器重排序規(guī)則和處理器內(nèi)存屏障插入策略來看,只要volatile變量與普通變量之間的重排序可能會破壞volatile的內(nèi)存語義,這種重排序就會被編譯器重排序規(guī)則和處理器內(nèi)存屏障插入策略禁止。
實(shí)現(xiàn)原理:
將當(dāng)前處理器緩存行的數(shù)據(jù)回寫到系統(tǒng)內(nèi)存。
寫回內(nèi)存的操作會使其他CPU里緩存該內(nèi)存地址的數(shù)據(jù)無效。
(2).synchronized
不同情況鎖住的對象:
對于普通同步方法,鎖是當(dāng)前實(shí)例對象。
對于靜態(tài)同步方法,鎖是當(dāng)前類的Class對象。
對于同步方法塊,鎖是Synchronized括號里配置的對象。
(3)final
public class FinalExample { int i; //普通變量 final int j; //final變量 static FinalExample obj; public FinalExample() { //構(gòu)造函數(shù) i = 1; //寫普通域 j = 2; //寫final域 } public static void writer() { obj = new FinalExample(); } public static void reader() { FinalExample object = obj; //讀對象引用 int a = object.i; //讀普通域 int n = object.j; //讀final域 } }
寫final域的重排序規(guī)則:JMM禁止將final域的寫重排序到構(gòu)造函數(shù)之外。
讀final域的重排序規(guī)則:在一個(gè)線程中,初次讀對象引用與初次讀該對象包含的final域,JMM禁止處理器重排序這兩個(gè)操作。
4.Java內(nèi)存模型(JMM)
5.重排序
重排序的3種類型:
編譯器優(yōu)化的重排序。
指令級并行的重排序。
內(nèi)存系統(tǒng)的重排序。
編譯器和處理器在重排序時(shí),會遵守?cái)?shù)據(jù)依賴性,編譯器和處理器不會改變存在數(shù)據(jù)依賴關(guān)系的兩個(gè)操作的執(zhí)行順序。
6.順序一致性
順序一致性內(nèi)存模型兩大特征:
一個(gè)線程中的所有操作必須按照程序的順序來執(zhí)行。
(不管程序是否同步)所有線程都只能看到一個(gè)單一的操作執(zhí)行順序。在順序一致性內(nèi)存模型中,每個(gè)操作都必須原子執(zhí)行且立刻對所有線程可見。
7.happens-before與as-if-serial
JMM對happens-before的設(shè)計(jì)原則:只要不改變程序的執(zhí)行結(jié)果(指的是單線程程序和正確同步的多線程程序),編譯器和處理器怎么優(yōu)化都行。
happens-before關(guān)系的定義:
(1)如果一個(gè)操作happens-before另一個(gè)操作,那么第一個(gè)操作的執(zhí)行結(jié)果將對第二個(gè)操作可見,而且第一個(gè)操作的執(zhí)行順序排在第二個(gè)操作之前。
(2)兩個(gè)操作之間存在happens-before關(guān)系,并不意味著Java平臺的具體實(shí)現(xiàn)必須要按照happens-before關(guān)系指定的順序來執(zhí)行。如果重排序之后的執(zhí)行結(jié)果,與按happens-before關(guān)系來執(zhí)行的結(jié)果一致,那么JMM允許這種重排序。
as-if-serial:不管怎么重排序(編譯器和處理器為了提高并行度),(單線程)程序的執(zhí)行結(jié)果不能被改變。
區(qū)別:as-if-serial語義保障單線程內(nèi)程序的執(zhí)行結(jié)果不被改變,happens-before關(guān)系保證正確同步的多線程程序的執(zhí)行結(jié)果不被改變。
8.雙重檢查鎖定與延遲初始化
使用雙重檢查鎖延遲初始化
public class DoubleCheckedLocking { private static DoubleCheckedLocking instance; public static DoubleCheckedLocking getInstance() { if(null == instance) { synchronized(DoubleCheckedLocking.class) { if(null == instance) { instance = new DoubleCheckedLocking(); } } } return instance; } }
線程A-A1:分配對象的內(nèi)存空間。
線程A-A2:設(shè)置instance指向內(nèi)存空間。
線程B-B1:判斷instance是否為空。
線程B-B2:由于instance不為null,線程B將訪問instance引用的對象。
線程A-A3:初始化對象
線程A-A4:訪問instance引用的對象。
存在的問題:
A2和A3重排序,線程B訪問到一個(gè)還沒初始化的對象。
解決方案:
將instance變量聲明為volatile型的。通過禁止重排序來保證線程安全的延遲初始化。
通過不允許其他線程”看到“這個(gè)重排序?qū)崿F(xiàn)線程安全的延遲初始化。
public class InstanceFactory { private static class InstanceHolder { public static InstanceFactory instance = new InstanceFactory(); } public static InstanceFactory getInstance() { return InstanceHolder.instance; } }
9.生產(chǎn)者-消費(fèi)者模式
(二).線程
1.什么是線程?
現(xiàn)代操作系統(tǒng)在運(yùn)行一個(gè)程序時(shí),會為其創(chuàng)建一個(gè)進(jìn)程。現(xiàn)代操作系統(tǒng)調(diào)度的最小單元是線程,也叫輕量級進(jìn)程。在一個(gè)進(jìn)程里可以創(chuàng)建多個(gè)線程,這些線程都擁有各自的計(jì)數(shù)器,堆棧和局部變量等屬性。
2.創(chuàng)建線程的三種方式
(1).Thread
@Test public void testThread() { Thread thread = new Thread("myThread"); thread.start(); }
(2).Runnable
@Test public void testRunnable() { Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("myThread"); } }); thread.start(); }
(3).Callable
@Test public void testFutureTask() throws InterruptedException, ExecutionException { ExecutorService executorService = Executors.newFixedThreadPool(2); Futurefuture = executorService.submit(new Callable () { @Override public String call() throws Exception { return "Hello,World!!!"; } }); String result = future.get(); System.out.println(result); executorService.shutdown(); }
3.Daemon線程
Daemon線程是一種支持型線程,因?yàn)樗饕挥米鞒绦蛑泻笈_調(diào)度以及支持性工作。
當(dāng)一個(gè)Java虛擬機(jī)中不存在非Daemon線程的時(shí)候,Java虛擬機(jī)將會退出。
可以通過調(diào)用Thread,setDaemon(true)將線程設(shè)置為Daemon線程。
4.等待/通知機(jī)制
等待/通知機(jī)制,是指一個(gè)線程A調(diào)用了對象O的wait()方法進(jìn)入等待狀態(tài),而另一個(gè)線程B調(diào)用了對象O的notify()或者notifyAll()方法,線程A收到通知后從對象O的wait()方法返回,進(jìn)而執(zhí)行后續(xù)操作。
等待方遵循如下規(guī)則:
獲取對象的鎖
如果條件不滿足,那么調(diào)用對象的wait()方法,被通知后仍要檢查條件。
條件滿足則執(zhí)行對應(yīng)的邏輯。
while(條件不滿足){ 對象.wait(); } //處理對應(yīng)的邏輯
通知方遵循如下規(guī)則:
獲得對象的鎖。
改變條件
通知所有等待在對象上的線程。
synchronized(對象){ //改變條件 對象.notifyAll(); }
5.Thread.join()
當(dāng)前線程A要等待thread線程終止之后才能從thread.join()返回。
6.ThreadLocal
ThreadLocal,即線程變量,是一個(gè)以ThreadLocal對象為鍵,任意對象為值的存儲結(jié)構(gòu)。這個(gè)結(jié)構(gòu)被附帶在線程上,也就是說一個(gè)線程可以根據(jù)一個(gè)ThreadLocal對象查詢到綁定到這個(gè)線程上的一個(gè)值。
7.線程的終止、中斷
(1).Thread.interrupt:中斷線程
除非線程正在進(jìn)行中斷它自身,否則都會接受這個(gè)方法的中斷。會調(diào)用Thread.checkAccess(),可能會拋出SecurityException。
如果線程調(diào)用了Object.wait(),Thread.sleep(),Thread.join()處于阻塞狀態(tài),那它的堵塞狀態(tài)會被清除,并得到一個(gè)InterruptedException。
如果線程在InterruptibleChannel上的I/O操作中被中斷,通道會被關(guān)閉,線程的中斷狀態(tài)會被設(shè)置,并得到一個(gè)ClosedByInterruptedException。
(2).Thread.interrupted:測試當(dāng)前線程是否被中斷。
清除線程的中斷狀態(tài)。如果連續(xù)調(diào)用兩次這個(gè)方法,第二次調(diào)用會返回false(除非當(dāng)前線程再次被中斷,在第一次調(diào)用清除它的中斷狀態(tài)之后,并且在第二次調(diào)用檢查它之前)。
(3).Thread.isInterrupted:測試某個(gè)線程是否被中斷
中斷狀態(tài)是否被重置取決于傳入的值。
(三).鎖
鎖是Java并發(fā)編程中最重要的同步機(jī)制。鎖除了讓臨界區(qū)互斥執(zhí)行外,還可以讓釋放鎖的線程向獲取同一個(gè)鎖的線程發(fā)送消息。
1.鎖的內(nèi)存語義:
利用volatile變量的寫-讀所具有的內(nèi)存語義。
利用CAS所附帶的volatile讀和volatile寫的內(nèi)存語義。
2.重入鎖
(1).什么是重入鎖?
支持重進(jìn)入的鎖,表示鎖能夠支持一個(gè)線程對資源的重復(fù)加鎖。重入鎖支持獲取鎖時(shí)的公平性和非公平性選擇。
(2).解決兩個(gè)問題:
線程再次獲取鎖:鎖需要去識別獲取鎖的線程是否為當(dāng)前占據(jù)鎖的線程,如果是,則再次獲取鎖。
鎖的最終釋放:鎖的最終釋放要求鎖對于鎖獲取進(jìn)行計(jì)數(shù)自增,計(jì)數(shù)表示當(dāng)前鎖被重復(fù)獲取的次數(shù),而鎖被釋放時(shí),計(jì)數(shù)自減,當(dāng)計(jì)數(shù)等于0時(shí)表示鎖已經(jīng)釋放。
3.排他鎖:ReentrantLock
(1)公平鎖:
公平鎖釋放時(shí),最后要寫一個(gè)volatile變量state。
公平鎖獲取時(shí),首先會去讀volatile變量。
(2)非公平鎖:
非公平鎖釋放時(shí),最后要寫一個(gè)volatile變量state。
非公平鎖獲取時(shí),首先會用CAS(CompareAndSet)更新volation變量,這個(gè)操作同時(shí)具有volatile寫和volatile讀的內(nèi)存語義。
(3)公平鎖與非公平鎖的區(qū)別
公平性與否是針對獲取鎖而言的。
公平鎖:如果一個(gè)鎖是公平的,那么獲取鎖的順序就應(yīng)該符合請求的絕對時(shí)間順序,也就是FIFO。
非公平鎖:剛釋放鎖的線程再次獲取同步狀態(tài)的幾率會非常大,使得其他線程只能在同步隊(duì)列中等待。
公平性鎖保證了鎖的獲取按照FIFO原則,而代價(jià)是進(jìn)行大量的線程切換。非公平性鎖雖然可能造成”饑餓“,但極少的線程切換,保證其更大的吞吐量。
4.Lock
(1)讀寫鎖:ReentrantReadWriteLock
讀寫鎖在同一時(shí)刻可以允許多個(gè)讀線程訪問,但是在寫線程訪問時(shí),所有的讀線程和其他寫線程均被堵塞。
讀寫鎖的實(shí)現(xiàn)分析:
讀寫狀態(tài)的設(shè)計(jì):同步狀態(tài)表示鎖被一個(gè)線程重復(fù)獲取的次數(shù),而讀寫鎖的自定義同步需要在同步狀態(tài)(一個(gè)整型變量)上維護(hù)多個(gè)讀線程和一個(gè)寫線程的狀態(tài),使得該狀態(tài)的設(shè)計(jì)成為讀寫鎖實(shí)現(xiàn)的關(guān)鍵。
寫鎖的獲取與釋放:寫鎖是一個(gè)支持重進(jìn)入的排它鎖。如果當(dāng)前線程已經(jīng)獲取了寫鎖,則增加寫狀態(tài)。如果當(dāng)前線程在獲取寫鎖時(shí),讀鎖已經(jīng)被獲?。ㄗx狀態(tài)不為0)或者該線程不是已經(jīng)獲取寫鎖的線程,則當(dāng)前線程進(jìn)入等待狀態(tài)。
讀鎖的獲取與釋放:如果當(dāng)前線程已經(jīng)獲取了讀鎖,就增加讀狀態(tài)。如果當(dāng)前線程在獲取讀鎖時(shí),寫鎖已被其他線程獲取,則進(jìn)入等待狀態(tài)。
鎖降級:鎖降級指的是寫鎖降級為讀鎖。指把持住(當(dāng)前擁有的)寫鎖,再獲取到讀鎖,隨后釋放(先前擁有的)讀鎖的過程。
鎖的四種狀態(tài):無鎖,偏向鎖,輕量級鎖,重量級鎖
5.LockSupport工具
當(dāng)需要阻塞或喚醒一個(gè)線程的時(shí)候,都會使用LockSupport工具類來完成相應(yīng)的工作。
6.Condition接口
Condition接口提供了類似Object的監(jiān)視器方法(包括wait(),wait(long timeout),notify(),以及notifyAll()方法),與Lock配合可以實(shí)現(xiàn)等待/通知模式。
Condition的實(shí)現(xiàn):等待隊(duì)列,等待和通知。
等待隊(duì)列:等待隊(duì)列是一個(gè)FIFO隊(duì)列,在隊(duì)列中的每一個(gè)節(jié)點(diǎn)都包含了一個(gè)線程引用,該線程是在Condition對象上等待的線程,如果一個(gè)線程調(diào)用了Condition.await()方法,那么該線程會釋放鎖,構(gòu)造成節(jié)點(diǎn)加入等待隊(duì)列并進(jìn)入等待狀態(tài)。
等待:調(diào)用Condition的await()方法(或者以await開頭的方法),會使當(dāng)前線程進(jìn)入等待隊(duì)列并釋放鎖,同時(shí)線程狀態(tài)變?yōu)榈却隣顟B(tài)。當(dāng)從await()方法返回時(shí),當(dāng)前線程一定獲取了Condition相關(guān)的鎖。
通知:調(diào)用Condition的signal()方法,將會喚醒在等待隊(duì)列中等待時(shí)間最長的節(jié)點(diǎn)(首節(jié)點(diǎn)),在喚醒節(jié)點(diǎn)之前,會將節(jié)點(diǎn)移步到同步隊(duì)列。
7.避免活躍性危險(xiǎn)
(1).死鎖
哲學(xué)家用餐問題:每個(gè)線程都擁有別的線程需要的資源,同時(shí)又等待別人擁有的資源,在獲得別的資源之前不會釋放自己手上的資源。
數(shù)據(jù)庫事務(wù)死鎖:數(shù)據(jù)庫如果發(fā)生死鎖,會選擇一個(gè)事務(wù)釋放資源。
鎖順序死鎖:線程A,B都需要鎖1,2。線程A先獲得鎖1 ,再請求鎖2 ,線程B先獲得鎖2,再請求鎖1 。
8.死鎖的避免與診斷
(1).內(nèi)置鎖:只要沒有獲得鎖,就會一直等待下去。
(2).定時(shí)鎖:使用Lock類的定時(shí)tyLock功能??梢灾付ㄒ粋€(gè)超時(shí)時(shí)限,在等待超過該時(shí)間后會返回失敗信息。
(3).線程轉(zhuǎn)儲
避免死鎖的四種方式:
避免一個(gè)線程同時(shí)獲得多個(gè)鎖。
避免一個(gè)線程在鎖內(nèi)同時(shí)占用多個(gè)資源,盡量保證每個(gè)鎖只獲得一個(gè)資源。
使用定時(shí)鎖。
對于數(shù)據(jù)庫鎖,加鎖和解鎖必須在一個(gè)數(shù)據(jù)庫連接里,否則會出現(xiàn)解鎖失敗的情況。
9.饑餓,糟糕的響應(yīng)性,活鎖
饑餓:線程由于無法訪問它需要的資源而不能繼續(xù)執(zhí)行,引發(fā)饑餓最常見的資源是CPU時(shí)鐘周期。
活鎖通常發(fā)送在處理事務(wù)消息的應(yīng)用程序中,如果不能成功地處理事務(wù)消息,那么消息機(jī)制將回滾整個(gè)事務(wù),將這個(gè)事務(wù)放在等待隊(duì)列的開頭。
當(dāng)多個(gè)相互協(xié)作的線程都對彼此響應(yīng)從而修改各自的狀態(tài),并使得任何一個(gè)線程都無法繼續(xù)執(zhí)行時(shí),就發(fā)生了活鎖。
在并發(fā)應(yīng)用中,通過等待隨機(jī)長度的時(shí)間和回退可以有效地避免活鎖的發(fā)生。
(四).同步器
(1).實(shí)現(xiàn)
同步隊(duì)列:同步器依賴內(nèi)部的同步隊(duì)列(一個(gè)FIFO雙向隊(duì)列)來完成同步狀態(tài)的管理,當(dāng)前線程獲得同步狀態(tài)失敗時(shí),同步器會將當(dāng)前線程以及等待狀態(tài)等信息構(gòu)造成為一個(gè)節(jié)點(diǎn)并將其加入同步隊(duì)列,同時(shí)會阻塞當(dāng)前線程,當(dāng)同步狀態(tài)釋放時(shí),會把首節(jié)點(diǎn)中的線程喚醒,使其再次嘗試獲取同步狀態(tài)。
獨(dú)占式同步狀態(tài)獲取與釋放:在獲取同步狀態(tài)時(shí),同步器維護(hù)一個(gè)隊(duì)列,獲取狀態(tài)失敗的線程會被加入隊(duì)列中并在隊(duì)列中進(jìn)行自旋,移除隊(duì)列的原因時(shí)自旋獲取了同步狀態(tài)且前驅(qū)節(jié)點(diǎn)時(shí)頭節(jié)點(diǎn)。在釋放同步狀態(tài)時(shí),同步器調(diào)用tryRelease(int arg)方法釋放同步狀態(tài),然后喚醒頭節(jié)點(diǎn)的后繼結(jié)點(diǎn)。
共享式同步狀態(tài)獲取與釋放:共享式獲取與獨(dú)占式獲取最主要的區(qū)別在于同一時(shí)刻能否有多個(gè)線程同時(shí)獲取同步狀態(tài)。
獨(dú)占式超時(shí)獲取同步狀態(tài):在指定的時(shí)間段內(nèi)獲取同步狀態(tài),如果獲取到同步狀態(tài)返回true,否則,返回false。
(2).AbstractQueuedSynchronized
用來構(gòu)建鎖或者其他同步組件的基礎(chǔ)框架,使用了一個(gè)int成員變量表示同步狀態(tài),通過內(nèi)置的FIFO隊(duì)列來完成資源獲取線程的排隊(duì)工作。
提供了三個(gè)對同步狀態(tài)進(jìn)行修改的方法:
getState():獲取當(dāng)前同步狀態(tài)。
setState(int new3State):設(shè)置當(dāng)前同步狀態(tài)。
compareAndSetState(int export,int update):使用CAS設(shè)置當(dāng)前狀態(tài),該方法能夠保證狀態(tài)設(shè)置的原子性。
(五).并發(fā)容器和框架
(1).ConcurrentHashMap
與HashMap,HashTable對比:
HashMap是線程不安全,且可能導(dǎo)致程序死循環(huán)。
HashTable效率低下。
ConcurrentHashMap的鎖分段技術(shù)可有效提升訪問效率。首先將數(shù)據(jù)分成一段一段的存儲,然后給每一段數(shù)據(jù)配一把鎖,當(dāng)一個(gè)線程占用鎖訪問其中一個(gè)段的數(shù)據(jù)的時(shí)候,其他段的數(shù)據(jù)也能被其他線程訪問。
ConCurrentHashMap的結(jié)構(gòu):ConCurrentHashMap是由Segment數(shù)組結(jié)構(gòu)和HashEntry數(shù)組結(jié)構(gòu)組成。
(2).ConcurrentLinkedQueue
ConcurrentLinkedQueue由head節(jié)點(diǎn)和tail節(jié)點(diǎn)組成,每個(gè)節(jié)點(diǎn)(Node)由節(jié)點(diǎn)元素(item)和指向下一個(gè)節(jié)點(diǎn)(next)的引用組成,從而組成一張鏈表結(jié)構(gòu)的隊(duì)列。
(3).阻塞隊(duì)列
插入:當(dāng)隊(duì)列滿時(shí),隊(duì)列會堵塞插入元素的線程,直到隊(duì)列不滿。
移除:當(dāng)隊(duì)列為空,獲取元素的線程會等待線程為非空。
實(shí)現(xiàn)原理:
使用通知模式實(shí)現(xiàn)。當(dāng)生產(chǎn)者往滿的隊(duì)列里添加元素時(shí)會堵塞生產(chǎn)者,當(dāng)消費(fèi)者消費(fèi)了一個(gè)隊(duì)列中的元素后,會通知生產(chǎn)者當(dāng)前隊(duì)列可用。
1.ArrayBlockingQueue
數(shù)組實(shí)現(xiàn)的有界阻塞隊(duì)列,按照先進(jìn)先出的原則對元素進(jìn)行排序。
2.LinkedBlockingQueue
繼承了AbstractQueue類,實(shí)現(xiàn)了BlockingQueue接口。
采用先進(jìn)先出的排列方式,頭結(jié)點(diǎn)是入隊(duì)時(shí)間最長的元素,尾結(jié)點(diǎn)是入隊(duì)時(shí)間最短的元素。新結(jié)點(diǎn)添加到隊(duì)尾,從隊(duì)頭彈出結(jié)點(diǎn)。
鏈表隊(duì)列的特點(diǎn)是:跟基于數(shù)組的隊(duì)列相比有更大的吞吐量,但在大多并發(fā)應(yīng)用中性能會比較差。
LinkedBlockingQueue可以在創(chuàng)建的時(shí)候傳遞一個(gè)容量參數(shù),限制隊(duì)列的長度,不設(shè)定的情況下,默認(rèn)是Integer.MAX_VALUE。在沒有超過隊(duì)列邊界的情況下,每次添加會自動(dòng)創(chuàng)建鏈表結(jié)點(diǎn)。
3.PriorityBlockingQueue
是一個(gè)支持優(yōu)先級的無界阻塞隊(duì)列。默認(rèn)情況下時(shí)自然順序升序排序。
4.SychronousQueue
SynchronousQueue是一個(gè)不存儲元素的堵塞隊(duì)列,每一個(gè)put操作必須等待一個(gè)take操作,否則不能繼續(xù)添加元素。
5.DelayQueue
延遲隊(duì)列:無界隊(duì)列,只有延遲過期的任務(wù)才能加入隊(duì)列。隊(duì)列的隊(duì)首元素是在過去延遲過期最長的元素。如果沒有延遲到期,隊(duì)列中就沒有元素,調(diào)用poll方法會返回null。當(dāng)調(diào)用元素的getDelay(TimeUnit.NANOSECONDS)方法返回等于或小于0的值時(shí),出現(xiàn)到期。
DelayQueue的應(yīng)用場景:a.緩存系統(tǒng):把需要緩存的元素加入DelayQueue中,讓一個(gè)線程循環(huán)測試是否能從DelayQueue中獲取元素,能表示緩存到期。b.定時(shí)任務(wù)調(diào)度。
Timer和DelayQueue的區(qū)別?Timer只能處理單個(gè)后臺線程,而DelayQueue可以處理多個(gè)。
6 . LinkedTransferQueue
LinkedTransferQueue是一個(gè)由鏈表結(jié)構(gòu)組成的無界阻塞TransferQueue隊(duì)列。
7 . LinkedBlockingDeque
一個(gè)由鏈表結(jié)構(gòu)組成的雙向阻塞隊(duì)列,可以運(yùn)用在“工作竊取”模式中。
(4).Fork/Join框架
用于并行執(zhí)行任務(wù),把一個(gè)大任務(wù)分割成小任務(wù),再把每個(gè)小任務(wù)的結(jié)果匯總成大任務(wù)結(jié)果。Fork是把一個(gè)大任務(wù)切分成若干子任務(wù)并行執(zhí)行,Join是合并這些子任務(wù)的執(zhí)行結(jié)果。
(5).工作竊取算法
指某個(gè)線程從其他隊(duì)列里竊取任務(wù)來執(zhí)行。為了減少竊取任務(wù)線程和別竊取任務(wù)線程之間的競爭,使用雙端隊(duì)列,被竊取任務(wù)線程從雙端隊(duì)列的頭部拿任務(wù)執(zhí)行,竊取任務(wù)線程從雙端隊(duì)列的尾部拿任務(wù)執(zhí)行。
工作竊取算法的優(yōu)點(diǎn):充分利用線程進(jìn)行并行計(jì)算,減少線程間的競爭。
工作竊取算法的缺點(diǎn):在某些情況下還是存在競爭,比如雙端隊(duì)列里只有一個(gè)任務(wù)時(shí),并且該算法會消耗更多的系統(tǒng)資源,比如創(chuàng)建多個(gè)線程和多個(gè)雙端隊(duì)列。
(六).Java并發(fā)工具類
1.CyclicBarrier
一組線程在到達(dá)一個(gè)屏障(同步點(diǎn))前被堵塞,直到最后一個(gè)線程到達(dá)屏障時(shí),屏障才會放行,這組線程才能繼續(xù)執(zhí)行。
應(yīng)用場景:可以用于多線程計(jì)算數(shù)據(jù),最后合并計(jì)算結(jié)果。
CyclicBarrier與CountDownLatch的區(qū)別:CountDownLatch的計(jì)數(shù)器只能使用一次,而CyclicBarrier的計(jì)數(shù)器可以使用reset()方法重置。CountDownLatch的計(jì)數(shù)是減法,CyclicBarrier的計(jì)數(shù)是加法。
2.Semaphore
用來控制同時(shí)訪問特定資源的線程數(shù)量,通過協(xié)調(diào)各個(gè)線程,以保證合理的使用公共資源。
應(yīng)用場景:可以用于流量控制,特別是公共資源有限的應(yīng)用場景,比如數(shù)據(jù)庫連接。
3.CountDownLatch
允許一個(gè)或多個(gè)線程等待其他線程完成操作。
4.Exchanger
Exchanger是一個(gè)用于線程間協(xié)作的工具類。Exchanger用于進(jìn)行線程間的數(shù)據(jù)交換。它提供一個(gè)同步點(diǎn),在這個(gè)同步點(diǎn),兩個(gè)線程可以交換彼此的數(shù)據(jù)。這兩個(gè)線程通過exchange方法交換數(shù)據(jù),如果第一個(gè)線程先執(zhí)行exchange()方法,會一直等待第二個(gè)線程也執(zhí)行exchange()方法,當(dāng)兩個(gè)線程都到達(dá)同步點(diǎn)時(shí),這兩個(gè)線程就可以交換數(shù)據(jù),將本線程生產(chǎn)出來的數(shù)據(jù)傳遞給對方。
應(yīng)用場景:用于遺傳算法。
(七).原子操作類
1.原子更新基本類型類
AtomicBoolean
AtomicInteger
AtomicLong
2.原子更新數(shù)組
AtomicIntegerArray
AtomicLongArray
AtomicReferenceArray
3.原子更新引用類型
AtomicReference
AtomicReferenceFieldUpdater 原子更新引用類型里的字段
AtomicMarkableReference 原子更新帶有標(biāo)記位的引用類型。
4.原子更新字段類
AtomicIntegerFieldUpdater 原子更新整型的字段的更新器
AtomicLongFieldUpdater 原子更新長整型字段的更新器
AtomicStampedReference 原子更新帶有版本號的引用類型。該類將整數(shù)值與引用關(guān)聯(lián)起來,可用于原子的更新數(shù)據(jù)和數(shù)據(jù)的版本號,可以解決使用CAS進(jìn)行原子更新時(shí)可能出現(xiàn)的ABA問題。
(八).Executor框架(執(zhí)行機(jī)制)
從JDK5開始,把工作單元和執(zhí)行機(jī)制分離開來,工作單元包括Runnable和Callable,而執(zhí)行機(jī)制由Executor框架提供。
1.異步計(jì)算的結(jié)果:FutureTask和Future
2.任務(wù)執(zhí)行
(1).Executor(核心接口)
Executor的生命周期:創(chuàng)建,提交,開始,完成
(2).ExecutorService接口(繼承自Executor)
ExecutorService的生命周期:運(yùn)行,關(guān)閉,已終止
ExecutorService.shutDown和ExecutorService.shutDownNow的區(qū)別
調(diào)用的方法 | 作用 |
---|---|
shutDown | 不再允許新的任務(wù)添加到等待隊(duì)列,正在執(zhí)行的任務(wù)和在等待隊(duì)列中的任務(wù)會執(zhí)行完畢再關(guān)閉。 |
shurDownNow | 立刻關(guān)閉。需要知道正在執(zhí)行但是還沒執(zhí)行完畢的任務(wù)。 |
ExecutorService.submit()和ExecutorService.execute()的區(qū)別:接口ExecutorService的execute()方法是繼承自Executor。
調(diào)用的方法 | 作用 |
---|---|
execute | 用于提交不需要返回值的任務(wù),所以無法判斷任務(wù)是否被線程池執(zhí)行成功。 |
submit | 用于提交需要返回值的任務(wù)。線程池會返回一個(gè)Future類型的對象,通過這個(gè)Future對象可以判斷任務(wù)是否執(zhí)行成功,并且通過Future的get()方法來獲取返回值,get()方法會阻塞線程直到任務(wù)完成。 |
ExecutorService的創(chuàng)建:
調(diào)用 | 分類 | 使用 |
---|---|---|
Executors.newSingleThreadExecutor() | ThreadPoolExecutor | 應(yīng)用場景:適用于需要保證順序地執(zhí)行各個(gè)任務(wù);并且在任意時(shí)間點(diǎn),不會有多個(gè)線程活動(dòng)的應(yīng)用場景。 |
Exectors.newFiexedThreadPool() | ThreadPoolExecutor | 1.創(chuàng)建一個(gè)線程池,具有固定線程數(shù),運(yùn)行在共享的無界隊(duì)列中。2.在大多數(shù)時(shí)候,線程會主動(dòng)執(zhí)行任務(wù),當(dāng)所有的線程都在執(zhí)行任務(wù)時(shí),有新的任務(wù)加入進(jìn)來,就會進(jìn)入等待隊(duì)列(可以有源源不斷的任務(wù)加入進(jìn)來,因?yàn)槭菬o界隊(duì)列),當(dāng)有空閑的線程,等待隊(duì)列中的任務(wù)就會被執(zhí)行。3.如果有線程在執(zhí)行過程中因?yàn)閳?zhí)行失敗要關(guān)閉,新創(chuàng)建的線程會替失敗的線程執(zhí)行接下來的任務(wù)。4.如果想要關(guān)閉這個(gè)線程池,可以調(diào)用ExecutorService的shutDown方法。應(yīng)用場景:適用于為了滿足資源管理的需求,而需要限制當(dāng)前線程數(shù)量的應(yīng)用場景,它適用于負(fù)載比較重的服務(wù)器。 |
Executors.newCachedThreadPool() | ThreadPoolExecutor | 應(yīng)用場景:適用于執(zhí)行很多短期異步任務(wù)的小程序,或者是負(fù)載較輕的服務(wù)器。 |
Executors.newScheduledThreadPool() | ScheduledThreadPoolExecutor | 應(yīng)用場景:適用于需要多個(gè)后臺線程執(zhí)行周期任務(wù),同時(shí)為了滿足管理的需求而需要限制后臺線程的數(shù)量的應(yīng)用場景。 |
Executors.newSingleThreadScheduledExecutor() | ScheduledThreadPoolExecutor | 應(yīng)用場景:需要單個(gè)后臺線程執(zhí)行周期任務(wù),同時(shí)需要保證順序地執(zhí)行各個(gè)任務(wù)。 |
3.任務(wù):Runnable接口和Callable接口
(九).其他
1.jstack
打開cmd,輸入:
jps 查看pid(進(jìn)程號)
jstack pid
2.資源限制:指在進(jìn)行并發(fā)編程時(shí),程序的執(zhí)行速度受到計(jì)算機(jī)硬件資源或軟件資源的限制。
3.上下文切換
減少上下文切換的方法:
無鎖并發(fā)編程
CAS算法
使用最少線程
使用協(xié)程
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/69538.html
摘要:方法由兩個(gè)參數(shù),表示期望的值,表示要給設(shè)置的新值。操作包含三個(gè)操作數(shù)內(nèi)存位置預(yù)期原值和新值。如果處的值尚未同時(shí)更改,則操作成功。中就使用了這樣的操作。上面操作還有一點(diǎn)是將事務(wù)范圍縮小了,也提升了系統(tǒng)并發(fā)處理的性能。 這是java高并發(fā)系列第21篇文章。 本文主要內(nèi)容 從網(wǎng)站計(jì)數(shù)器實(shí)現(xiàn)中一步步引出CAS操作 介紹java中的CAS及CAS可能存在的問題 悲觀鎖和樂觀鎖的一些介紹及數(shù)據(jù)庫...
摘要:筆記來源并發(fā)編程與高并發(fā)解決方案并發(fā)基礎(chǔ)綜述多級緩存緩存一致性亂序執(zhí)行優(yōu)化內(nèi)存模型規(guī)定抽象結(jié)構(gòu)同步八種操作及規(guī)則并發(fā)的優(yōu)勢與風(fēng)險(xiǎn)并發(fā)與高并發(fā)基本概念基本概念并發(fā)同時(shí)擁有兩個(gè)或者多個(gè)線程,如果程序在單核處理器上運(yùn)行,多個(gè)線程將交替地?fù)Q入或者換 筆記來源:【IMOOC】Java并發(fā)編程與高并發(fā)解決方案 并發(fā)基礎(chǔ) 綜述: CPU多級緩存:緩存一致性、亂序執(zhí)行優(yōu)化 Java內(nèi)存模型:JM...
高級并發(fā)對象 到目前為止,本課程重點(diǎn)關(guān)注從一開始就是Java平臺一部分的低級別API,這些API適用于非?;A(chǔ)的任務(wù),但更高級的任務(wù)需要更高級別的構(gòu)建塊,對于充分利用當(dāng)今多處理器和多核系統(tǒng)的大規(guī)模并發(fā)應(yīng)用程序尤其如此。 在本節(jié)中,我們將介紹Java平臺5.0版中引入的一些高級并發(fā)功能,大多數(shù)這些功能都在新的java.util.concurrent包中實(shí)現(xiàn),Java集合框架中還有新的并發(fā)數(shù)據(jù)結(jié)構(gòu)。 ...
摘要:相比與其他操作系統(tǒng)包括其他類系統(tǒng)有很多的優(yōu)點(diǎn),其中有一項(xiàng)就是,其上下文切換和模式切換的時(shí)間消耗非常少。因?yàn)槎嗑€程競爭鎖時(shí)會引起上下文切換。減少線程的使用。很多編程語言中都有協(xié)程。所以如何避免死鎖的產(chǎn)生,在我們使用并發(fā)編程時(shí)至關(guān)重要。 系列文章傳送門: Java多線程學(xué)習(xí)(一)Java多線程入門 Java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(1) java多線程學(xué)習(xí)(二)syn...
摘要:在中一般來說通過來創(chuàng)建所需要的線程池,如高并發(fā)原理初探后端掘金閱前熱身為了更加形象的說明同步異步阻塞非阻塞,我們以小明去買奶茶為例。 AbstractQueuedSynchronizer 超詳細(xì)原理解析 - 后端 - 掘金今天我們來研究學(xué)習(xí)一下AbstractQueuedSynchronizer類的相關(guān)原理,java.util.concurrent包中很多類都依賴于這個(gè)類所提供的隊(duì)列式...
摘要:在中一般來說通過來創(chuàng)建所需要的線程池,如高并發(fā)原理初探后端掘金閱前熱身為了更加形象的說明同步異步阻塞非阻塞,我們以小明去買奶茶為例。 AbstractQueuedSynchronizer 超詳細(xì)原理解析 - 后端 - 掘金今天我們來研究學(xué)習(xí)一下AbstractQueuedSynchronizer類的相關(guān)原理,java.util.concurrent包中很多類都依賴于這個(gè)類所提供的隊(duì)列式...
閱讀 3469·2019-08-30 13:15
閱讀 1405·2019-08-29 18:34
閱讀 832·2019-08-29 15:18
閱讀 3489·2019-08-29 11:21
閱讀 3252·2019-08-29 10:55
閱讀 3707·2019-08-26 10:36
閱讀 1875·2019-08-23 18:37
閱讀 1832·2019-08-23 16:57