1.減小上下文切換的方法有無鎖併發編程、CAS算法、使用最少線程和使用協程。程序員
無所併發編程:多線程競爭鎖時,會引發上下文切換,因此多線程處理數據時,能夠用一些辦法來避免使用鎖,如將數據的ID按照Hash算法取模分段,不一樣的線程處理不一樣段的數據;算法
CAS算法:Java的Atomic包使用CAS算法來更新數據,而不須要加鎖;數據庫
使用最少線程:避免建立不須要的線程,好比任務不多,可是建立了不少線程來處理,這樣會形成大量線程處於等待狀態;編程
協程:在單線程裏實現多任務的調度,並在單線程裏維持多個任務間的切換。小程序
2.避免死鎖的幾個常見方法:數組
避免一個線程同時獲取多個鎖; 避免一個線程在鎖內同時佔用多個資源,儘可能保證每一個鎖只佔用一個資源; 嘗試使用定時鎖,使用lock.tryLock(timeout)來替代使用內部鎖機制; 對於數據庫鎖,加鎖和解鎖必須在一個數據庫連接裏,不然會出現解鎖失敗的狀況。 3.在Java中,鎖一共有四種,級別從低到高一次爲:無所狀態,偏向鎖狀態,輕量級鎖狀態和重量級鎖狀態。這幾個狀態會隨着競爭狀況逐漸升級,鎖能夠升級但不能降級,意味着偏向鎖升級爲輕量級鎖後不能降級爲偏向鎖。緩存
4.在大多數狀況下,鎖不只不存在多線程競爭,並且老是由同一線程屢次得到,爲了讓線程得到鎖的代價更低而引入了偏向鎖。當一個線程訪問同步代碼塊獲取鎖時,會在對象頭和棧幀中的鎖記錄裏存儲鎖偏向的線程ID,之後該線程在進入和退出同步塊時不須要進行CAS操做來加鎖和解鎖,只需簡單地測試一下對象頭的Mark Word裏是否存儲着指向當前線程的偏向鎖。若是測試成功,表示線程已經得到了鎖。若是測試失敗,則須要再測試一下Mark Word中偏向鎖的標識是否設置成了1(表示當前是偏向鎖):若是沒有設置,則使用CAS競爭鎖;若是設置了,則嘗試使用CAS將對象頭的偏向鎖指向當前線程。安全
5.偏向鎖使用了一種等到競爭出現才釋放鎖的機制,因此當其餘線程嘗試競爭偏向鎖時,持有偏向鎖的線程纔會釋放。偏向鎖的撤銷,須要等待全局安全點(在這個時間點上沒有正在執行了字節碼)。它會首先暫停擁有偏向鎖的線程,而後檢查持有偏向鎖的線程是否活着,若是線程不處於活動狀態,則將對象頭設置成無鎖狀態;若是線程仍然活着,擁有偏向鎖的棧會被執行,遍歷偏向對象的鎖記錄,棧中的鎖記錄和對象頭的Mark Word要麼從新偏向於其餘線程,要麼恢復到無鎖或者標記對象不適合做爲偏向鎖,最後喚醒暫停的線程。服務器
6.由於自旋會消耗CPU,爲了不無用的自旋(好比得到鎖的線程被阻塞住了),一旦鎖升級成重量級鎖,就不會再恢復到輕量級鎖狀態。當鎖處於這個狀態下,其餘線程試圖獲取鎖時,都會被阻塞住,當持有鎖的線程釋放鎖以後會喚醒這些線程,被喚醒的線程會進行新一輪的奪鎖之爭。數據結構
7.原子操做意爲「不可被中斷的一個或一系列操做」。
8.術語定義: 緩存行(Cache line):緩存的最小操做單位; 比較並交換(Compare and Swap, CAS):CAS操做須要輸入兩個數值,一箇舊值(指望操做前的值)和一個新值,在操做期間先比較舊值有沒有發生變化,若是沒有發生變化,才交換成新值,發生了變化則不交換; CPU流水線(CPU pipeline):CPU流水線的工做方式就像工業生產上的裝配流水線,在CPU中由5~6個不一樣功能的電路單元組成一條指令處理流水線,而後將一條X86指令分紅5~6步後再由這些電路單元分別執行,這樣就能實如今一個CPU時鐘週期完成一條指令,所以提升CPU的運算速度。 內存順序衝突(Memory order violation):內存順序衝突通常是由假共享引發的,假共享是指多個CPU同時修改同一個緩存行的不一樣部分而引發其中一個CPU的操做無效,當出現這個內存順序衝突時,CPU必須清空流水線。
9.處理器實現原子操做的方式:
經過總線鎖保證原子性。總線鎖就是使用處理器提供的一個LOCK #信號,當一個處理器在總線上輸出此信號時,其餘處理器的請求將被阻塞,那麼該處理器能夠獨佔共享內存。 經過緩存鎖定來保證原子性。頻繁使用的內存會緩存再處理器的L一、L2和L3高速緩存裏,那麼原子操做就能夠直接在處理器內部緩存中進行,並不須要聲明總線鎖。所謂「緩存鎖定」是指內存區域若是被緩存再處理器的緩存行中,而且在Lock操做期間被鎖定,那麼當它執行鎖操做會寫到內存時,處理器不在總線上聲言LOCK #信號,而是修改內部內存地址,並容許它的緩存一致性機制來保證操做的原子性,由於緩存一致性機制會阻止同時修改由兩個以上處理器緩存的內存區域數據,當其餘處理器回寫已被鎖定的緩存行的數據時,會使緩存行無效。 10.若是一個操做執行的結果須要對另外一個操做可見,那麼兩個操做之間必需要存在happens-before關係。
11.happens-before規則以下:
程序順序規則:一個線程中的每一個操做,happens-before於該線程中的任意後續操做; 監視器鎖規則:對一個鎖的解鎖,happens-before於隨後對這個鎖的加鎖; volatile變量規則:對一個volatile變量的寫,happens-before於任意後續對這個volatile變量的讀; 傳遞性:若是A happens-before B,B happens-before C,那麼A happens-before C; 12.happens-before僅僅要求前一個操做(執行的結果)對後一個操做可見,且前一個操做按順序排在第二個操做以前。
13.重排序:重排序是指編譯器和處理器爲了優化程序性能而對指令序列而進行從新排序的一種手段。
14.編譯器和處理器會對操做進行重排序,編譯器和處理在重排序時,會遵照數據依賴性,編譯器和處理器不會改變存在數據依賴關係的兩個操做的執行順序;這裏所說的數據依賴性僅針對單個處理器中執行的指令序列和單個線程中執行的操做,不一樣處理器之間和不一樣線程之間的數據依賴性不被編譯器和處理器考慮。
15.as-if-serial語義的意思是:無論怎麼重排序(編譯器和處理器爲了提升並行度),(單線程)程序的執行結果不能被改變,編譯器,runtime和處理器都必須遵照as-if-serial語義。
16.在計算機中,軟件技術和硬件技術有一個共同的目標:在不改變程序執行結果的前提下,儘量提升並行度。
17.當代碼中存在控制依賴性時,會影響指令序列執行的並行度。爲此,編譯器和處理器會採用猜想執行來客戶控制相關性對並行度的影響。
18.在單線程程序中,對存在控制依賴的程序重排序,不會改變執行結果(這也是as-if-serial語義容許對存在控制依賴的操做進行重排序的緣由);但在多線程程序中,對存在控制依賴的操做重排序,可能會改變程序的執行結果。
19.數據競爭的定義:在一個線程中寫一個變量,在另外一個線程中讀同一個變量,並且寫和讀沒有經過同步來排序。
20.若是程序是正確同步的,程序的執行將具備順序一致性--即該程序的執行結果與程序在順序一致性模型中的執行結果相同。
21.順序一致性內存模型的兩大特性:
一個線程中的全部操做必須按照程序的順序來執行; (無論程序是否同步)全部線程都只能看到一個單一的操做執行順序。在順序一致性內存模型中,每一個操做都必須原子執行且馬上對全部線程可見。 22.在Java內存模型中,臨界區內的代碼能夠重排序(但Java內存模型不容許臨界區內的代碼逸出到臨界區以外,那樣會破壞監視器的語義)。Java內存模型會在退出臨界區和進入臨界區這兩個關鍵時間點作一些特殊處理,使得線程在這兩個時間點具備與順序一致性模型相同的內存視圖。
23.Java內存模型與順序一致性模型的差異:
順序一致性模型保證單線程內的操做會按程序的順序執行,而Java內存模型不保證單線程內的操做按照程序順序執行; 順序一致性模型保證全部線程只能看到一致的操做執行順序,而Java內存模型不保證全部線程能看到一致的操做執行順序; Java內存模型不保證對64位的long和double類型變量寫操做具備原子性,而順序一致性模型保證對全部的內存讀寫操做都具備原子性。 24.64位long和double類型變量的寫操做不具備原子性的緣由是:在一些32位的機器上,對於內存的寫入操做,每次只能是原子的執行一段32位內存的寫入,對於64位的變量,須要分兩次原子的寫入操做進行,於是總體來講是不具有原子性的。
25.在jdk1.5版本之前,一個64位long/double類型變量的讀/寫操做能夠拆分爲兩個32位的讀/寫操做來進行;在jdk1.5版本(含)以後,64位的long/double類型變量的寫入操做可以拆分爲兩個32位的寫操做來進行,而讀操做則經過jvm保證其具備原子性。
26.volatile變量的特性:
可見性。一個volatile變量的讀,老是可以看到最後一個線程對volatile變量寫入的值; 原子性。對任意單個volatile變量的讀/寫具備原子性,可是相似於volatile++這種複合操做不具有原子性。 27.當讀一個volatile變量時,Java內存模型會把該線程對應的本地內存置爲無效,而從主內存中讀取共享變量。
28.volatile寫和volatile讀的內存語義:
線程A寫一個volatile變量,實質上是線程A向接下來所要讀這個volatile變量的線程發出了(其對共享變量所做修改的)信息; 線程B讀一個volatile變量,實質上是線程B接收了以前某個線程發出的(在寫這個volatile變量以前對共享變量所作修改的)信息; 線程A寫一個volatile變量,隨後線程B讀一個volatile變量,這個過程實質上是線程A經過主內存向線程B發送消息。 29.volatile變量讀寫操做重排序的規則:
當第二個操做是volatile寫時,無論第一個操做是什麼,都不能重排序。這個規則確保volatile寫以前的操做不會被重排序到volatile寫以後。 當第一個操做是volatile讀時,無論第二個操做是什麼,都不能重排序。這個規則確保volatile讀以後的操做不會被重排序到volatile讀以前。 當第一個操做是volatile寫,第二個操做是volatile讀時,不能重排序。 30.Java內存模型對於volatile變量插入內存屏障的策略:
在每一個volatile寫操做以前插入一個StoreStore屏障; 在每一個volatile寫操做後面插入一個StoreLoad屏障; 在每一個volatile讀操做後面插入一個LoadLoad屏障; 在每一個volatile讀操做後面插入一個LoadStore屏障; 31.final域的重排序規則:
在構造函數中對一個final域的寫入,與隨後把這個被構造對象的引用賦值給一個引用變量,這兩個操做之間不能重排序; 初次讀一個包含final域的對象的引用,與隨後初次讀這個final域,這兩個操做之間不能重排序。 32.經過爲final域增長寫和讀重排序規則,能夠爲Java程序員提供初始化安全保證:只要對象是正確構造的(被構造對象的引用在對象構造過程當中沒有逸出),那麼不須要使用同步(指lock和volatile的使用)就能夠保證任意線程均可以看到這個final域在構造函數中被初始化以後的值。
33.對於會改變程序執行結果的重排序,Java內存模型要求編譯器和處理器必須禁止這種重排序;對於不會改變程序執行結果的重排序,Java內存模型對編譯器和處理器不作要求(Java內存模型容許這種重排序)。
34.Java內存模型對編譯器和處理器已經儘量少。從上面的分析能夠看出,Java內存模型實際上是在遵循一個基本原則:只要不改變程序的執行結果(指的是單線程程序和正確同步的多線程程序),編譯器和處理器怎麼優化都行。
35.若是一個操做happens-before另外一個操做,那麼第一個操做的執行結果將對第二個操做可見,並且第一個操做的執行順序排在第二個操做以前;兩個操做之間存在happens-before關係,並不意味着Java平臺的具體實現必須按照happens-before關係指定的順序來執行。若是重排序以後的執行結果,與按happens-before關係來執行的結果一致,那麼這種重排序並不非法(也就是說Java內存模型容許這種重排序)。
36.jsr-133中定義的happens-before規則:
程序順序規則:一個線程中的每一個操做,happens-before於該線程中的任意後續操做; 監視器鎖規則:對一個鎖的解鎖,happens-before於後續對該鎖的加鎖; volatile變量規則:對一個volatile域的寫,happens-before於任意後續對這個volatile域的讀; 傳遞性:若是A happens-before B,且B happens-before C,那麼A happens-before C; start規則:若是線程A執行操做ThreadB.start()(啓動線程B),那麼A線程中的ThreadB.start()操做happens-before於線程B中的任意操做; join規則:若是線程A執行ThreadB.join()併成功返回,那麼線程B中的任意操做happens-before於線程A從ThreadB.join()操做成功返回。 37.當一個Java虛擬機中不存在非Daemon線程的時候,Java虛擬機將會退出。
38.Daemon屬性須要在啓動線程以前設置,不能再啓動線程以後設置。
39.Daemon線程被用做完成支持性工做,可是在Java虛擬機退出時Daemon線程中的finally塊並不必定會執行,於是在構建Daemon線程時,不能依靠finally塊中的內容來確保執行關閉或清理資源的邏輯。
40.線程start()方法的含義是:當前線程(即parent線程)同步告知Java虛擬機,只要線程規劃器空閒,應當即啓動調用start()方法的線程。
41.在啓動一個線程時,最好爲這個線程設置一個名稱,這樣在進行jstack分析時可方便問題排查。
42.調用wait()、notify()和notifyAll()須要注意的細節:
使用wait()、notify()和notifyAll()時須要對調用對象加鎖; 調用wait()方法後,線程狀態由RUNNING變成WAITING,並將當前線程放置到對象的等待隊列; notify()和notifyAll()調用以後,線程依舊不會從wait()返回,須要調用notify()或notifyAll()的線程釋放鎖以後,等待線程纔有機會從wait()返回; notify()將等待隊列中的一個線程從等待隊列移到同步隊列中,而notifyAll()則是將等待隊列中全部線程從等待隊列移到同步隊列中,被移動的線程狀態由WAITING變爲BLOCKED; 從wait()方法返回的前提是得到了調用對象的鎖。 43.Wait Notify的經典範式: ①等待方遵循以下規則:
獲取對象的鎖; 若是條件不知足,那麼調用對象的wait()方法,被通知後仍要檢查條件; 條件知足則執行對應的邏輯; 對應僞代碼以下: synchronized (對象) { while (條件不知足) { 對象.wait();
} 對象的處理邏輯 } ②通知方遵循以下原則:
得到對象的鎖; 改變條件; 通知全部等待在對象上的線程; 對應僞代碼以下: synchronized (對象) { 改變條件; 對象.notifyAll(); } 44.等待超時模式的僞代碼:
public synchronized Object get(long mills) throws InterruptedException { long future = System.currentTimeMillis() + mills; long remaining = mills;
// 當超時大於0而且result返回值不知足要求
while ((result == null) && remaining > 0) {
wait(remaining);
remaining = future - System.currentTimeMillis();
}
return result;
複製代碼
} 45.若是在絕對時間上,先對鎖進行獲取的請求必定先被知足,那麼這個鎖是公平的,反之,是不公平的。事實上,公平鎖的效率沒有非公平鎖的效率高,可是公平鎖可以減小「飢餓」發生的機率。
46.實現鎖的重進入須要解決的問題:
線程再次獲取鎖。鎖須要去識別獲取鎖的線程是否爲當前佔據鎖的線程,若是是,則再次成功獲取; 鎖的最終釋放。線程重複n此獲取了鎖,隨後在第n此釋放該鎖後,其餘線程可以獲取到該鎖。鎖的最終釋放要求鎖對於獲取進行計數自增,計數表示當前鎖被重複獲取的次數,而鎖被釋放時,計數自減,當計數等於0時,表示鎖已經成功釋放。 47.讀寫鎖在同一時刻能夠容許多個線程訪問,可是在寫線程訪問時,全部的讀線程和其餘寫線程均被阻塞。讀寫鎖維護了一對鎖:一個讀鎖和一個寫鎖,經過分離讀鎖和寫鎖,使得併發性相比通常的排他鎖有了很大提高。
48.讀寫鎖將變量切分紅了兩個部分,高16位表示讀,低16位表示寫。
49.ReentrantReadWriteLock使用位來表徵讀寫鎖狀態的方式:假設當前同步狀態值爲S,寫狀態等於S&0x0000FFFF(將高16位所有抹去),讀狀態等於S>>>16(無符號補0右移16位)。當寫狀態增長1時,等於S+1,當讀狀態增長1時,等於S+(1<<16),也就是S+0x00010000
50.根據上述狀態劃分得出一個結論:S不等於0時,當寫狀態(S&0x0000FFFF)等於0時,則讀狀態(S>>>16)大於0,即讀鎖已被獲取。
51.對於ReentrantReadWriteLock,只有等待其餘讀線程都釋放了讀鎖,寫鎖才能被當前線程獲取,而寫鎖一旦被獲取,則其餘讀寫線程的後續訪問均被阻塞。
52.鎖降級指的是把持住(當前擁有的)寫鎖,再獲取到讀鎖,隨後釋放(先前擁有的)寫鎖的過程。
53.LockSupport定義了一組以park開頭的方法用來阻塞當前線程,以及unpark(Thread thread)方法來喚醒一個被阻塞的線程。
54.通常都會將Condition對象做爲成員變量。當調用await()方法後,當前線程會釋放鎖並在此等待,而其餘線程調用Condition對象的signal()方法,通知當前線程後,當前線程才從await()方法返回,並在返回前獲取到了鎖。
55.在Condition的等待隊列中並無使用CAS算法來保證新加入的節點是加入到節點尾部,由於調用Condition.await()方法的線程一定是獲取了鎖的線程,其是經過鎖來保證線程安全的。
56.多線程會致使HashMap的Entry鏈表造成環形數據結構,一旦造成環形數據結構,Entry的next節點將永不爲空,就會產生死循環獲取Entry。
57.HashTable容器使用synchronized來保證線程安全,當一個線程訪問HashTable的同步方法時,其餘線程訪問HashTable的任何方法都將進入阻塞或輪詢狀態。
58.ConcurrentHashMap容器中有多把鎖,每一把鎖只鎖定一部分數據,多線程訪問時,對於不一樣段數據的訪問不須要競爭同一把鎖,從而能夠有效的提升併發訪問效率,這就是ConcurrentHashMap的鎖分段技術。
59.ConcurrentHashMap在爲Segament擴容的時候,首先會建立一個長度是原來兩倍的數組,而後經過再散列將原數組中的數據散列到新的數組中。爲了高效,ConcurrentHashMap不會對Segament進行擴容。
60.ConcurrentHashMap計算size的方式以下:在Segament對象中有一個字段count,該字段記錄了當前Segament中全部的鍵值對的數目,在調用size()方法時經過累加每一個Segament中的count,從而獲得總的count,可是因爲在累加的過程當中可能以前累加的count發生了變化,於是ConcurrentHashMap經過兩次累加每一個Segament中的count,並比較兩次獲得的數值是否相同,若是相同則返回,不然將全部對錶進行結構性修改的操做都鎖住,而後進行一次累加獲得結果。
61.阻塞隊列中方法說明: 阻塞隊列方法說明
62.各個阻塞隊列說明: ①ArrayBlockingQueue:其是用一個數組實現的有界阻塞隊列,按照先進先出的方式排序; ②LinkedBlockingQueue:其是一個用鏈表實現的有界阻塞隊列,此隊列的默認和最大長度爲Integer.MAX_VALUE; ③PriorityBlockingQueue:其是一個支持優先級的無界阻塞隊列,默認狀況下按照天然順序升序排序,若是指定了Comparator則按照指定的排序方式排序,須要注意的是其不能保證同優先級元素的順序; ④DelayedQueue:其是一個支持延遲獲取元素的無界阻塞隊列,隊列使用PriorityQueue實現,隊列中的元素必須實現Delayed接口,在建立元素時能夠指定多久才能從隊列中獲取到元素,只有延遲期滿時才能從隊列中提取元素; ⑤SynchronousQueue:其是一個不存儲元素的阻塞隊列,每一個put操做必須等待一個take操做,不然不能添加元素,其也支持公平和非公平策略訪問隊列(該隊列的吞吐量高於LinkedBlockingQueue和ArrayBlockingQueue); ⑥LinkedTransferQueue:其是一個有鏈表結構組成的無界阻塞TransferQueue隊列,相比於其餘阻塞隊列,其多了tryTransfer和transfer方法,對於transfer方法,若是當前有正在等待的消費者,transfer方法會將生產者傳入的元素當即transfer到消費者,若是沒有等待的消費者,其會將元素放入到隊列的tail節點,對於tryTransfer方法,其是用來試探生產者傳入的元素可否直接傳給消費者,若是有則返回true,不然返回false,對於有時間限的tryTransfer方法,若是有正在等待的消費者,該方法直接返回true,若是沒有,則等待設定的時間,仍是沒有則返回false; ⑦LinkedBlockingDeque:其是一個由鏈表結構組成的雙向阻塞隊列,便可以從隊列頭和隊列尾兩端插入和移除元素,雙向隊列多了一個隊列入隊和出隊的口,於是在多線程入隊和出隊時也就減小了一半的競爭。
63.DelayedQueue的兩個應用場景: ①緩存系統的設計:能夠用DelayedQueue設計緩存系統,將元素放置到DelayedQueue中後,若是在指定時間以後能從DelayedQueue中獲取到元素,那麼說明元素過時了; ②定時任務調度:能夠將一些定時任務放到DelayedQueue中,經過一個線程輪詢隊列,當從隊列中取到元素以後則執行該任務。
64.實現Delayed接口的方式(分三步): ①在建立對象時,初始化基本數據,使用time記錄當前對象延遲到何時使用,使用sequenceNumber來標識元素在隊列中的前後順序; ②實現getDelay方法,該方法返回當前元素還須要延時多長時間,單位是納秒; ③實現compareTo方法來指定元素的順序,讓延時時間最長的放在隊列的末尾。
65.park方法會阻塞當前線程,其在如下四種狀況下會返回:
與park對應的unpark方法執行或已經執行,「已經執行」是指unpark先執行,而後執行park方法; 線程被中斷; 等待完time參數指定的毫秒數; 異常現象發生時,這個異常現象沒有任何緣由。 66.Fork/Join框架是一個把大任務分隔成若干個小任務,最終彙總每一個小任務結果後獲得大任務結果的框架。
67.工做竊取算法:工做竊取算法是指某個線程從其餘隊列裏竊取任務來執行,爲了減小與正常執行線程之間的競爭,其通常是使用雙端隊列來完成。在Fork/Join框架中,通常fork出來的子任務是放在不一樣的隊列中的,而且每一個隊列有一個線程執行其中的任務,若是某個線程執行得較快,任務已作完,其不能一直等待着其餘的隊列線程執行完任務以後才合併結果,於是須要使用工做竊取算法使其同步執行其餘隊列的任務。
68.工做竊取算法的優勢:充分利用線程進行並行計算,減小線程間的競爭;缺點:在某些狀況下仍是存在競爭,好比雙端隊列只有一個任務時。而且該算法消耗了更多的系統資源,好比建立了多個線程和多個雙端隊列。
69.Fork/Join框架的設計:
步驟一:分割任務。首先須要一個fork類來吧大任務分割成子任務,有可能子任務仍是很大,因此還須要不停的分割,直到分割出的子任務足夠小; 步驟二:執行任務併合並結果:分割的子任務分別放在雙端隊列裏,而後幾個啓動線程分別從雙端隊列裏獲取任務執行。子任務執行完的結果統一放在一個隊列裏,啓動一個線程從隊列裏拿數據,而後合併這些數據。 70.Fork/Join框架的主要類:
ForkJoinTask:該類表示要執行的任務,使用時咱們主要繼承其兩個子類:RecursiveAction和RecursiveTask。RecursiveAction表示沒有返回值的任務,RecursiveTask表示有返回值的任務; ForkJoinPool:執行ForkJoinTask的類。 71.ForkJoinTask在執行時,若是拋出異常,那麼主線程是沒法捕獲到該異常的,於是該類提供了一個isCompletedAbnormally方法,用於檢測是否正常完成,而且提供了一個getException方法,若是當前任務被取消了,那麼該方法將返回CancellationException,若是任務沒有完成或拋出異常,則返回null。
72.AtomicIntegerArray的構造函數能夠傳入一個整形數組,也能夠傳入一個length,其會在內部將傳入的數組複製一份或者新建一個length長度的數組,於是對AtomicIntegerArray的操做不會影響原數組的值。
73.原子更新字段類AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicStampedReference使用時須要注意兩點:
由於原子更新字段類都是抽象類,每次使用的時候必須使用靜態方法newUpdater()建立一個更新器,而且須要設置想要更新的類和屬性; 更新類的字段(屬性)必須使用public volatile修飾符。 74.AtomicStampedReference是原子更新帶有版本號的引用類型,該類將整數值與引用關聯起來,更新引用的時候也會原子的更新版本號,從而解決了使用CAS進行原子更新時可能出現的ABA問題。
75.CountDownLatch容許一個或多個線程等待其餘線程完成任務以後纔會繼續往下執行,其內部維護了一個整數變量,每次調用countDown()方法,該整數變量都會減一,而另外的調用await()方法的線程將會被阻塞,直到屢次調用countDown()方法後內部維護的整數變量值減爲0了。
76.CountDownLatch在構造時須要傳入一個整數,每次調用該類對象的countDown()方法時內部維護的該整數就會減一,而CountDownLatch::await()方法會阻塞當前線程,直到其內部維護的整數值降爲0。這裏countDown()方法的調用能夠是一個線程調用屢次,也能夠是多個線程每一個調用一次,只要調用該整數值次便可。
77.CountDownLatch構造函數傳入的整數值必須大於等於0.
78.CyclicBarrier的做用是讓一組線程到達一個屏障(也能夠說是一個同步點)時阻塞,直到全部的線程都到達了該屏障,而後才讓全部線程繼續往下執行。
79.CyclicBarrier另外提供了一個以下構造函數CyclicBarrier(int parties, Runnable barrier-action),第一個參數仍是指將要阻塞的線程數量,而第二個參數指定了一個任務,該任務會由第一個到達屏障的線程執行,可是其是在全部阻塞的線程執行當前任務到達屏障以後準備繼續執行後續任務時才執行,也即全部阻塞的線程從await()方法中返回的時候。
80.Semaphore(信號量)是用來控制同時訪問特定資源的線程數量,它經過協調各個線程,以保證合理的使用公共資源。
81.線程池的優勢:
下降資源消耗。經過重複利用已建立的線程下降線程建立和銷燬形成的消耗; 提升響應速度。當任務到達時,任務能夠部須要等到線程建立就能當即執行; 提升線程的可管理性。線程是稀缺資源,若是無限制的建立,不只會消耗系統資源,還會下降系統的穩定性,使用線程池能夠進行統一分配、調優和監控。 82.提交任務時線程池的處理流程以下: ①線程池判斷核心線程池裏的線程是否都在執行任務。若是不是,則建立一個新的線程來執行任務,若是核心線程池裏的線程都在執行任務,則進入下一個流程; ②線程池判斷工做隊列是否已滿。若是工做隊列沒有滿,則將新提交的任務存儲在這個工做隊列裏,若是工做隊列滿了,則進入下一個流程; ③線程池判斷線程池的線程是否都處於工做狀態,若是沒有,則建立一個新的線程來執行任務。若是已經滿了,則交給飽和策略來處理這個任務。
83.CachedThreadPool是大小無界的線程池,適用於執行不少的短時間異步任務的小程序,或者是負載較輕的服務器。
84.FixedThreadPool適用於爲了知足資源管理的需求,而須要限制當前線程數量的應用場景,它適用於負載比較重的服務器。
85.SingleThreadExecutor適用於須要保證順序執行各個任務,而且在任意時間點,不會有多個線程是活動的應用場景。
86.使用無界隊列對FixedThreadPool帶來了以下影響:
當線程池中的線程達到corePoolSize以後,新任務將在無界隊列中等待,所以線程池中的線程數不會超過corePoolSize,於是設置的maximumPoolSize將是一個無效參數; 因爲任務會一直添加到無界隊列中,而且除了corePoolSize個線程以後不會建立新的線程,於是設置的keepAliveTime和RejectedExecutionHandler將不會產生影響。 87.CachedThreadPool底層是建立了一個ThreadPoolExecutor,其corePoolSize傳入的是0,maxPoolSize是Integer.MAX_VALUE,空閒等待時間是60s,而且其使用的SynchronousQueue來處理任務。這裏SynchronousQueue內部是沒有容量保存任何任務的,每一個線程提交了一個任務以後必須有一個線程取出任務。從這些參數能夠看出,初始狀態下CachedThreadPool中是沒有任何線程的,當提交一個新的任務以後若是當前有空閒的線程,則該線程取出任務執行,若是沒有線程空閒,那麼就會建立一個新的線程,也就是說若是須要執行的任務很是多,那麼建立的線程數將會急劇上升。於是CachedThreadPool適用於多任務數,而且執行時間較短的場景。
88.FutureTask表示一個將要執行的任務,其不只實現了Future接口,還實現了Runnable接口,於是能夠將其當作一個任務提交給Executor執行。FutureTask的任務有三種狀態:未啓動、已啓動和已完成。對於已完成狀態,其可能有三種狀態:FutureTask::run()方法正常結束,run()方法被取消而結束,run()方法拋出異常結束。
89.當FutureTask處於未啓動或已啓動狀態時,FutureTask::get()方法將致使線程阻塞;當FutureTask處於已完成狀態時,FutureTask::get()方法將致使線程當即返回或拋出異常。
90.當FutureTask處於未啓動狀態時,FutureTask.cancel()方法將直接取消該任務的執行;當其處於已啓動狀態時,若是執行FutureTask::cancel(true),那麼將以中斷此任務執行線程的方式中止任務執行,若是執行FutureTask::cancel(false),那麼其不會對正在執行此任務的線程產生影響;當FutureTask處於已完成狀態時,執行FutureTask::cancel()方法將返回false。
91.在實際應用中,若是須要繼承某個已有的類或抽象類,而基於「組合優先於繼承」的原則,咱們能夠在要建立的類中聲明一個內部類來繼承須要繼承的類,而後將當前類與內部類使用組合來解耦。
92.生產者和消費者是經過一個容器來解決生產者和消費者的強耦合問題。生產者和消費者彼此之間不直接通訊,而是經過阻塞隊列來進行通訊,因此生產者生產完數據以後不用等待消費者處理,直接扔給阻塞隊列,消費者不找生產者要數據,而是直接從阻塞隊列裏取,阻塞隊列就至關於一個緩衝區,平衡了生產者和消費者的處理能力。