1.競態條件:多個線程共享相同的內存地址空間,而且併發執行下發生訪問或修改其餘線程正在使用的變量,而致使結果不一致。
2.活躍性:某件正確的事情最終發生,但不夠好,所以須要解決性能問題
3.在多線程中,當線程調度器臨時掛起活躍線程並轉而運行另一個線程時,就會產生頻繁的上下文切換(Context Switch),將會帶來極大的開銷,
保存和恢復執行上下文,丟失局部性,而且cpu時間將更多地花在線程調度而不是線程運行上。
線程安全性:(核心:正確性,當多個線程訪問某個類時,這個類始終都能表現出正確的行爲)
1.編寫線程安全的代碼,核心在於對狀態訪問操做進行管理,特別是共享的和可變的狀態的訪問。
2.同步機制:synchronized:獨佔的加鎖方式
3.多線程訪問同一個可變的狀態變量沒有使用合適的同步,會出現錯誤,修復方式:
a.不在線程之間共享該狀態變量
b.將狀態變量改成不可變的變量
c.在訪問變量時使用同步
4.競態條件:因爲不恰當的執行時序而出現不正確的結果(最常出現的類型:先檢查後執行)
5.實際狀況中,應儘量地使用現有的線程安全對象(如AtomicLong)來管理類的狀態,以確保準確性
6.內置鎖:
每一個java對象均可用做一個實現同步的鎖;互斥體(互斥鎖)最多隻有一個線程能持有這種鎖
a.同步代碼塊(synchronized):一做爲鎖的對象引用,一個做爲由這個鎖保護的代碼塊;
b.重入性:若是某個線程試圖得到一個已經由它持有的鎖,那麼這個請求就會成功
7.常見的加鎖約定(如Vector):將全部的可變狀態都封裝在對象內部,並經過對象的內置鎖對全部訪問可變狀態的代碼路徑
進行同步,使得在該對象上不會發生併發訪問
8.重排序:a、編譯器和處理器不會改變存在數據依賴性關係的兩個操做的執行順序
b、單線程下,不能改變數據的執行結果
9.最低安全性:當線程在沒有同步的狀況下讀取變量時,可能會獲得一個失效值,但至少這個是由以前某個線程設置的值,而不是隨機值。
10.加鎖與可見性:爲確保全部線程都能看到共享變量的最新之值,全部執行讀操做或寫操做的線程都必須在同一個鎖上同步。
11.Volatile變量:當把變量聲明爲volatile類型後,編譯器與運行時都會注意到該變量時共享的,所以不會將該變量的操做與其餘內存操做
一塊兒重排序;也不會被緩存在寄存器或者處理器不可見的地方,所以總返回最新寫入的值。
缺點:不足以確保遞增操做的原子性(count++),加鎖能既可確保可見性又能夠確保原子性,而volaitile只能確保可見性
12.調試:對於服務器應用程序,不管在開發階段仍是測試階段,當啓動jvm時必定要指定-server命令行選項。server模式的JVM比clienr模式的
進行了更多的優化
13.發佈:是一個對象能在當前做用域以外的代碼中使用,最簡單的方法(使用靜態修飾)
逸出:某個不該該被髮布的對象卻被髮布了
14.線程封閉(JDBC的Connection對象):訪問共享數據時,須要同步,而避免使用同步就是不共享數據或者單線程內訪問數據
Ad-hoc線程封閉(不推薦):維護線程封閉性的職責徹底由程序實現來承擔;線程封閉技術場景:一般是由於要將某個特定的子系統實現爲一個單線程子系統
15.棧封閉:只能經過局部變量才能訪問對象,而且確保對象不會逸出
16.ThreadLocal類(常做爲上下文等使用):是線程中的某個值與保存值的對象關聯,但在java5.0中,已被每次調用時分配一個新的緩衝區
替代。
17.不可變對象(必定是線程安全的):某個對象在被建立後其狀態就不能再被修改 @Immutable修飾
條件:1.對象建立之後其狀態就不能修改 2.對象的全部域都是final類型 3.對象是正確建立的(在對象的建立期間,this引用沒有逸出)
18.Final:final類型的域是不能修改的,但若是修飾的是對象的引用,則引用的對象是可修改的。
編程習慣:除非須要某個域是可變的,不然應將其聲明爲final域
19.要安全發佈一個對象,且對象的引用及對象的狀態必須同時對其餘線程可見。
java中常見的:Hashtable、synchronizedMap或concurrentHashMap等
1.在靜態初始化函數中初始化一個對象引用 2.將對象的引用保存到volatile類型的域或者AtomicReferance對象中
3.將對象的引用保存到某個正確構造對象的final類型域中 4.將對象的引用保存到一個由鎖保護的域中
20.事實不可變對:對象從技術上來看是可變的,但其狀態在發佈後不會再改變
21.併發程序中使用和共享對象策略:
a.線程封閉:線程封閉的獨享只能由一個線程擁有,對象被封閉在該線程中,而且只能由這個線程修改
b.只讀共享:在沒有額外同步的狀況下,共享的只讀對象可由多個線程併發訪問,但任何線程都不能修改;
共享的只讀對象包括不可變對象和事實不可變對象
c.線程安全共享:線程安全的對象在其內部實現同步,多線程可經過對象的公有接口來進行訪問而不須要進一步的同步
d.保護對象:被保護的對象只能經過持有特定的鎖來訪問
4、對象的組合:
1.設計線程安全的類基本要素:
a.找出構成對象狀態的全部變量 b.找出約束狀態變量的不變性條件(final類型的域使用得越多,越能簡化對象可能狀態的分析過程) c.創建對象狀態的併發訪問管理策略
2.狀態的全部權:
3.實例封閉:對象被封閉在類的實例中或者一個做用域中或者線程內,而不是被多個線程之間共享
4.監聽器模式:
·········
5.若是一個狀態變量是線程安全的,而且沒有任何不變性條件來約束它的值,在變量的操做上也不存在任何不容許的狀態轉換,則能夠
安全地發佈該變量
6.Collections.synchronizedList等方法可對集合進行加鎖操做
5、基礎構建模塊:
1.同步容器類:Vector和Hashtable
2.ConcurrentModificationException:容器在
迭代過程當中被修改時(當對象之間從容器中刪除而不是經過Iterator.remove來刪除),會拋出此異常
3.隱藏迭代器: 編譯器會把字符串的鏈接操做轉化爲調用StringBuilder.append(Object),而此方法又會調用容器的toString(),所以可能
出現
ConcurrentModificationException
4.HashMap.get或List.contains可能包含的工做:當遍歷散列桶或鏈表查找某個特定的對象時,必須在許多元素上調用equals
ConcurrentHashMap:再也不是將每一個方法都在同一個鎖上同步並使得每次只能有一個線程訪問;而是使用分段鎖這種更細粒度的加鎖機制
來實現更大程度的共享。再也不會拋出
ConcurrentModificationException
5.中斷是一種協做機制,一個線程不能強制其餘線程中止正在執行的操做而去執行其餘的操做。
當調用方法拋出InterruptedException時處理策略:
a.傳遞InterruptedException,不捕獲該異常,或者捕獲該異常,再在執行某種簡單的清理工做後再次拋出該異常。
b.恢復中斷,有時不能拋出InterruptedException,如Runnable中必須捕獲InterruptedException,並經過調用當前線程上的interrupt方法恢復中斷狀態。
a.閉鎖:能夠延遲線程的進度直到其到達終止狀態,一次性對象,一旦進入終止狀態,就不能被重置。
CountDownLatch:可使一個或多個線程等待一組事件發生。 countDown():遞減計數器,表示有一個事件已經發生
await():若是計數器的值非零,await會一直阻塞直到計數器爲零,或者等待中的線程中斷,或者等待超時。當計數器達到零,表示全部須要等待的事件都已經發生
b.FutureTask:表示一種抽象的可生成結果的計算(經過Callable來實現),可處於等待執行、正在執行、運行完成三種狀態
future.get行爲取決於任務的狀態;若是任務已完成,則當即返回結果;不然將阻塞直到任務進入完成狀態。
Callable表示的任務可拋出受檢查或未受檢查的異常,而且任何代碼均可能拋出一個Error,最後被封裝到ExecutionException中,並在Future.get中從新拋出。
c.計數信號量(Semaphore):用於控制同時訪問某個特定資源的操做數量,或者執行某個指定操做的數量。場景:資源池、容器的邊界
d.柵欄:相似於閉鎖,能阻塞一組線程直到某個事件發生
與閉鎖區別:全部線程必須同時到達柵欄位置,才能繼續執行;閉鎖用於等待事件,而柵欄用於等待其餘線程。
六 任務執行:
1.爲提升處理效率,無限建立線程的不足:
a.線程生命週期的開銷很是高 b.資源消耗,活躍的線程會消耗系統資源,尤爲是內存
c.穩定性,可建立線程的數量上存在一個限制(如JVM的啓動參數、Thread構造函數中請求的棧大小、底層系統對線程的限制等,破壞限制可能oom)
2.Executor框架:JVM只有在全部線程(非守護)所有終止後纔會退出,若是沒法正確關閉Executor,則JVM將沒法結束
a.基於生產者-消費者模式,提交任務的操做至關於生產者,執行任務的線程至關於消費者
b.線程池:重用現有的線程而不是建立新線程,可在處理多個請求時分攤在線程建立和銷燬過程當中產生的巨大開銷。
c.
newFixedThreadPool():返回固定數量的線程池,該方法的線程數始終不變,當有任務提交,若是線程池中空閒,則當即執行,
若沒有則會被暫緩在一個任務隊列中等待有空閒的線程再執行
newSingleThreadExecutor ():建立一個線程的線程池,若空閒則執行,若沒有空閒線程則暫緩在任務隊列中
newCachedThreadPool():可根據實際狀況調整線程個數的線程池,不限制最大線程數量,如有任務則建立線程,
若無任務則不建立線程。若是無任務則線程在60s後自動回收(默認),
超過處理需求,則回收空閒的線程
newScheduledThreadPool():返回一個SchededExecutorService對象,但線程池可指定線程的數量。
2.Timer負責管理延遲任務及週期任務,但
在執行定時任務時只會建立一個線程,若是某個任務的執行時間過長,則會破壞其餘TimeTask的定時精確性
;
若是TimerTask拋出RuntimeException,Timer會中止全部任務的運行(線程泄露);Timer執行週期任務時依賴系統時間;而ScheduledExecutorService基於時間的延遲,不會因爲系統時間的改變發生執行變化。
另外若是要構建本身的調度服務,可使用DelayQueue,只有某個元素逾期後,才能從DelayQueue中執行take操做。
3.Executor框架(生命週期:建立、提交、開始和完成)使用Runnable做爲任務的基本形式,但Runnable不能返回一個值或拋出一個受檢查異常;所以更推薦使用Callable;
Future表示一個任務的聲明週期,並提供判斷是否已經完成和獲取任務的結果等方法;get方法取決於任務的狀態(還沒有開始、正在運行、已完成);若是任務已完成,則當即返回或拋出一個Exception;若是任務沒完成,則會阻塞並直到任務完成
4.ExecutorService中全部submit方法都將返回一個Future,從而將一個Runnable或Callable提交給Executor,獲得Future來得到任務的執行結果或取消任務
5.CompletionService將Executor和BlockingQueue的功能融合在一塊兒,可將Callable任務提交給其執行,再使用take和poll來得到已完成的結果。
ExecutorCompletionService實現CompletionService,在構造函數中建立一個BlockingQueue來保存結果,當計算完調用Future-Task的done方法;當提交某個任務時,該任務將首先包裝成一個QueuingFuture,再改寫子類的done方法,結果放入BlockingQueue中。
6.中斷:不會真正地中斷一個正在運行的線程,而是發起中斷請求,由線程在下一個合適的時刻中斷本身。
處理中斷:除非須要屏蔽中斷,不然應拋出InterruptedException或者經過再次調用interrupt來恢復中斷狀態
a.傳遞異常 b.恢復中斷狀態,從而使調用棧中的上層代碼能進行處理
中斷策略:某種形式的線程級或者服務級取消操做:應儘快退出,在必要時清理,通知某個全部者線程已經退出。
編程習慣:出現異常時,取消那些再也不須要結果的任務
7.處理不可中斷的阻塞:
a.Java.io包中的同步Socket I/O 最多見的阻塞I/O形式就是對套接字進行讀取和寫入,對InputStream和OutputStream關閉底層套接字來拋出一個SocketException
b.I/O 當中斷一個正在InterruptibleChannel(大多數chanel都實現了該標準)上等待的線程,將使其及該鏈路上的線程
拋出CloseByInterruptException
當關閉一個InterriotibleChannel是將致使鏈路操做上阻塞的線程都跑出AsynchrousCloseException
c.Selector的異步I/O 若是一個線程在調用Selector.select(nio包)方法時阻塞了,則調用close或wakeup會使線程拋出CloseSelectorException並返回
d.獲取某個鎖 Lock類中提供了lockInterruptibly來容許在等待一個鎖的同時仍能響應中斷
e.中止基於線程的服務:除非擁有某個線程,不然不能對該線程進行操控,通常由線程池來關閉。
f.關閉ExecutorService:shutdown正常關閉和shutdownNow強行關閉(關閉當前正在執行的任務,再返回全部
還沒有啓動的任務清單)
h."毒丸"對象:放在一個隊列上的對象,當獲得這對象時,當即中止;場景:只有在生產者和消費者的數量都已知的狀況下,纔可以使用"毒丸"對象
i.經過execute提交的任務,才能將拋出的異常交給未捕獲異常處理器;而經過submit提交的任務因爲拋出了異常而結束,則該異常將被Future.get封裝在ExecutionException從新拋出(被認爲是返回狀態的一部分)。
j.JVM關閉:正常關閉中,JVM先調用全部已註冊的關閉鉤子(Shutdown Hook:經過
Runtime.addShutdownHook註冊的但還沒有開始的線程),
當全部的關閉鉤子都執行結束時,若是runFinalizersOnExit爲true,則JVM運行終結器,而後再中止。
k.線程:普通線程和守護線程;差別:當一個線程退出時,jvm會檢查其餘正在運行的線程,若是都是守護線程,則會正常退出;當jvm中止時,全部仍然存在的守護線程都將被拋棄(既不會執行finally,也不會執行回捲棧,而是直接退出)
七.線程池的使用:
1.只有當任務都是同類型的而且相互獨立時,線程池的性能才能達到最佳。若是將運行時間較長的任務與較短的任務混合在一塊兒,除非線程池很大,不然將可能形成「擁塞」;若是提交的任務依賴於其餘任務,除非線程池無限大,不然將可能形成死鎖。、
2.線程飢餓死鎖:線程池中的任務須要無限期地等待一些必須由池中其餘任務才能提供的資源或條件才能繼續執行,那麼除非線程池足夠大,不然就會發生線程飢餓死鎖
3.緩解長時間任務形成的影響,即限定任務等待資源的時間,而不無限制等待(限時版本和無限時版本);如Thread.join、BlockingQueue.put
、CountDownLatch.await及Selector.select等
4.設置線程池大小,參考Runtime.availableProcessors來動態計算;
對於計算密集型的任務,在擁有N個處理器的系統,線程池的大小爲N+1時,一般能實現最優的利用率。
5.常見線程池:ThreadPoolExecutor建立初期,線程並不會當即啓動,而是等到有任務提交時才啓動,除非調用prestartAllCoreThreads;newCacheThreadPool(
SynchronousQueue)將線程池的最大大小設置爲Integer.MAX_VALUE,基本大小爲0,此線程池理論上可無限擴展,且當需求下降時會自動收縮。
6.ThreadPoolExecutor容許提供一個BlockingQueue來保存等待執行的任務,基本的任務排隊方式:無界隊列(LinkedBlockingQueue)、有界隊列(ArrayBlockingQueue)和同步移交(SynchronousQueue不是一個真正的隊列,而是一種在線程之間進行移交的機制)
SynchronousQueue(只有當線程池是無界或可拒絕任務時有價值)不是一個真正的隊列,而是一種在線程之間進行移交的機制,能夠用來避免任務排隊及直接將任務從生產者移交給工做線程,要將元素放入SynchronousQueue中,必須由另外一個線程正在等待接收該元素。
PriorityBlockingQueue按優先級來安排任務,優先級經過天然排序或Comparator來定義
7.飽和策略:ThreadPoolExecutor可經過setRejectedExecutionHandler來修改,若是某個任務被提交到一個已關閉的Executor中,也會用到飽和策略。常見的飽和策略:
a.AbortPolicy(默認):會拋出未檢查的RejectedExecutionException,調用者可捕獲該異常,再處理
b.CallerRunsPolicy:既不拋棄任務也不拋出異常,而是將任務回退到調用者,從而下降新任務流量
c.DiscardPolicy:當新提交的任務沒法保存到隊列中等待執行時,會被悄悄拋棄
d.DiscardOldestPolicy:拋棄下一個將被執行的任務,而後嘗試從新提交新的任務(若是工做隊列是個優先隊列,則會拋棄優先級最高的任務,所以不要和優先級隊列一塊兒使用)
8.線程工廠:每當線程池須要建立一個新線程,都是經過線程工廠方法完成,ThreadFactory.newThread(Runnable r);
9.擴展ThreadPoolExecutor:經過改寫beforeExecute、afterExecute和terminated;不管任務時從run中正常返回仍是拋出一個異常返回,afterExecute都會被調用(若是任務完成後帶有一個Error,則不會調用afterExecute);beforeExecute同理
10.避免活躍性危險:
1.死鎖(高負載下易發生)
:
兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們都將沒法推動下去
鎖順序死鎖(LeftRightDeadLock):兩個線程試圖以不一樣的順序來得到相同的鎖。改用相同順序來獲取鎖。
制定鎖順序時,可以使用System.identityHashCode方法獲取hashCode來決定。
2.活躍性問題:若是在持有鎖時調用某個外部方法,而外部方法還可能會獲取其餘的鎖,或阻塞時間過長,致使其餘線程沒法及時得到當前被持有的鎖,就可能產生死鎖
3.開放調用:在調用某個方法時不須要持有鎖
4.支持定時的鎖:Lock的tryLock可指定一個超時時限,在等待超過該時間後會返回一個失敗信息
5.儘可能避免使用線程優先級,由於會增長平臺依賴性,並可能致使活躍性(死鎖)問題(大多數併發程序中,均可直接使用默認的線程優先級)
6.另外一種活躍性問題:活鎖(一般發生於處理事務消息的程序),不會阻塞線程,但也不能繼續執行,由於線程將不斷重複執行相同的操做,並且總會失敗;避免:等待隨機長度的時間和回退
11.性能與可伸縮性:
1.性能:服務時間、延遲時間、吞吐率等來衡量-->perfbar可給出CPU的忙碌程序信息
可伸縮性:當增長計算資源時(如CPU、內存)程序的吞吐量或者處理能力相應地增長
2.線程上下文切換:若是可運行的線程大於CPU的數量,則操做系統最終會將某個正在運行的線程調度出來,使得其餘線程能使用CPU,這操做將致使一次上下文切換:保存當前運行線程的執行上下文,並將新調度進來的線程的執行上下文設置爲當前上下文
大多數通用的處理器上,上下文切換開銷至關於5000-1000個時鐘週期(幾微秒)
3.內存同步:在synchronized和volatile提供的可見性中,內存柵欄扮演着刷新緩存,使緩存無效,刷新硬件的寫緩衝及中止執行管道角色
jvm優化沒必要要的鎖:synchronized(new Object())
逸出分析:abc被封閉在棧中且都會自動成爲線程本地變量,不會發生逸出操做,編譯器會去掉那3次鎖獲取操做
4.阻塞:若是等待事件較短,可採用自旋等待方式;若是等待時間較長,則考慮線程掛起
5.減小鎖的競爭:併發程序中,對可伸縮性最主要的威脅就是獨佔方式的資源鎖。
鎖競爭緣由:鎖的請求頻率及每次持有該鎖的時間
下降鎖的競爭程度:1.減小鎖的持有時間 2.下降鎖的請求頻率 3.使用帶有協調機制的獨佔鎖
鎖分解:將一個鎖分解成兩個鎖
鎖分段:將一個鎖分解爲多個鎖;對一組對象上的鎖進行分解-->concurrentHashMap;
劣勢:與採用單個鎖來實現獨佔訪問相比,要獲取多個
鎖來實現獨佔訪問將更加困難而且開銷更高
6.ReadWriteLock:若是多個讀取操做都不會修改共享資源,則這些讀取操做可同時訪問該共享資源,但在執行寫入操做時必須以獨佔方式來獲取鎖
13.顯示鎖:
1.
Lock:提供了一種無條件的、可輪詢的、定時的以及可中斷的鎖獲取操做,全部的加鎖和解鎖方法都是顯式的
ReentrantLock:加鎖時,必須手動釋放鎖,提供了與synchronized相同的互斥性和內存可見性,還爲鎖的不可用性問題提供了更高的靈活性
2.tryLock:輪詢和定時獲取鎖
lockInterruptibly:能在獲取鎖的同時保持對中斷的相應且包含於Lock鎖中,無須建立其餘類型的不可中斷阻塞機制
內置鎖中,釋放鎖的操做老是與獲取鎖的操做處於同一代碼塊,而不考慮控制權如何退出該代碼塊
非塊結構的鎖:ReentrantLock
越多的資源被耗費在鎖的管理和調度上,則應用程度獲得的資源就越少,最終致使性能降低:考慮ReentrantLock
3.公平性:
公平鎖:線程按照其發出請求的順序來得到鎖,新請求的鎖將被放入隊列等待;在執行加鎖時,會因爲掛起線程和恢復線程時存在的開銷而極大下降性能。tryLock仍會"插隊";當持有鎖的時間較長,或請求鎖的平均時間間隔較長,則應使用公平鎖
非公平鎖:容許「插隊」,當一個線程請求一個非公平鎖時,若是在發出請求的同時該鎖的狀態變爲可用,則跳過隊列中等待的線程並得到該鎖。
一般狀況下,性能會高於公平鎖的性能,緣由:在恢復一個被掛起的線程與該線程真正開始運行之間存在着嚴重的延遲。
4.synchronized和ReentrantLock選擇:若是忘記調用unlock將埋下隱患,所以當須要可定時、輪詢的與可中斷的鎖獲取操做、公平隊列、以及非塊結構的鎖時用ReentrantLock(獲取鎖的操做不能與棧幀關聯,而內置鎖能夠);不然優先用synchronized:優勢:在線程轉儲中能給出哪些調用幀得到了哪些鎖,並能檢測和識別發生死鎖的線程;
5.讀寫鎖:ReadWriteLock:當讀取由其保護的數據時,必須先得到讀取鎖;當修改時,必須先得到寫入鎖;
容許多個讀操做同時進行,但每次只容許一個寫操做;
讀寫交互:
ReentrantReadWriteLock:公平的鎖中,等待時間最長的線程將優先得到鎖;若是鎖由讀線程持有,另外一個線程請求寫入鎖,則其餘讀線程都不能得到讀取鎖,直到寫線程徹底使用完且釋放了寫入鎖。
非公平鎖中,寫線程降級爲讀線程是能夠的,但從讀線程升級爲寫線程則不能夠(會致使死鎖)
14.自定義的同步工具:
1.ArrayBlockingQueue:有界緩存:put:不能將元素放入已滿的緩存中
take:
不能從空緩存中獲取元素
2.條件隊列:使一組線程可以經過某種方式來等待特定的條件爲真
當調用wait()、notify()或notifyAll等,必定要持有與條件隊列相關的鎖
Object.wait()會自動釋放鎖,並請求操做系統掛起當前線程,從而使其餘線程可以得到這個鎖並修改對象的狀態。
若是某個功能沒法經過輪詢和休眠來實現,則條件隊列也沒法實現,但條件隊列使得在表達和管理狀態依賴性時更加簡單和高效
3.條件謂詞:使某個操做成爲狀態依賴操做的前提條件
條件等待:加鎖、wait方法和一個條件謂詞;另外鎖對象與條件隊列對象(即調用wait()和notify()等方法所在的對象)必須是同一個對象
4.另外一種活躍性(死活、活鎖等)故障:丟失的信號:線程必須等待已經爲真的條件,但在開始等待以前沒有檢查條件謂詞
5.只有當知足如下兩種條件時才能使用notify而不是notifyAll:
a.全部等待線程的類型都相同:只有一個條件謂詞與條件隊列相關,而且每一個線程在從wait返回後將執行相同的操做
b.單進單出 在條件變量上的每次通知,最多隻能喚醒一個線程來執行
6.顯式Condition對象:廣義上的內置條件隊列
Lock和Condition一般搭配使用:Lock.newCondition() Condition對Object進行了擴展,包含wait和notify,await、signal和signalAll分別對應於wait、
notify、notifyAll;
7.AbstractQueuedSynchronizer:實現閉鎖?是一個Java提升的底層同步工具類,用一個int類型的變量表示同步狀態,並提供了一系列的CAS操做來管理這個同步狀態。AQS的主要做用是爲Java中的併發同步組件提供統一的底層支持,例如ReentrantLock,CountdowLatch就是基於AQS實現的,用法是經過繼承AQS實現其模版方法,而後將子類做爲同步組件的內部類。
8.AQS:
15.原子變量與非阻塞同步機制:
1.原子變量適用於計數器、序列發生器和統計數據收集等,同時還能比基於鎖的方法提供更高的伸縮性
2.CAS包含三個操做數:需讀寫的內存位置V、進行比較的值A和擬寫入的新值B。當且僅當V的值等於等於A時,CAS纔會用原子方式將新值
來更新V的值,不然不會執行任何操做;不管位置V的值是否等於A,都將返回V原有的值
缺點:將使調用者處理競爭問題(經過重試、回退、放棄),而在鎖中能自動處理競爭問題(線程在得到鎖以前將一直阻塞)
3.原子變量類:12個,4組:標量類(Scalar,擴展了Number類如Integer或Long)、更新器類、數組類及複合變量類
最經常使用:AtomicInteger
(支持算術運算)、
AtomicLong(支持算術運算)、
AtomicBoolean、
AtomicReference 都支持CAS
4.AtomicInteger:提供了get和set方法、compareAndSet(old,new)
5.非阻塞算法:一個線程的失敗或掛起不會致使其餘線程失敗或掛起
6.無鎖算法(Lock-Free):在算法的每一個步驟中都存在某個線程可以執行下去
7.ABA問題:可經過增長一個版本號來解決
8.重排序:
9.偏序關係(Happens-Before):想保證執行操做B的線程看到操做A的結果(不管A和B是否在同一個線程中執行),則A和B之間必須知足Happens-Before關係;不然JVM可對它們任意地重排序
a.程序順序規則:若是程序中操做A在操做B以前,則線程A操做將在B操做以前執行
b.監視器鎖規則:在監視器鎖上的解鎖操做必須在同一個監視器鎖上的加鎖操做以前執行
c.volatile變量規則:對volatile變量的寫入操做必須在該變量的讀操做以前
d.線程啓動規則:在線程上對Thread.Start的調用必須在該線程執行任何操做以前執行
e.線程結束規則:線程中的任何操做都必須在其餘線程檢測到該線程已經結束以前執行或者Thread.join成功返回或者Thread.isAlive返回false
f.中斷規則:當一個線程在另外一個線程上調用interrupt時,必須在中斷線程檢測到interrupt以前執行(經過InterruptedException或isInterrupted和interrupted)
g:終結器規則:對象的構造函數必須在啓動該對象的終結器以前執行完成
h:傳遞性:A->B->C,A需在C以前執行
10.全序:同步操做(鎖的獲取與釋放)及volatile的讀取與寫入
11.糟糕的雙重檢查加鎖:當在沒同步的狀況下讀取一個共享對象,可能看到只是一個失效值;將list聲明爲volatile