本文地址:http://www.cnblogs.com/maplefighting/p/7941885.html html
一、volatile:輕量級的synchronized,不會引發線程上下問切換java
爲了提升速度,處理器不直接和內存進行通訊,而是先將系統內存的數據讀到內部緩存後再操做。聲明瞭volatile,jvm向處理器大宋Lock前綴指令,將變量在緩存行的數據寫到系統內存,每一個處理器經過總線傳播的數據檢查本身緩存的值是否過時。node
二、volatile實現原則:算法
(1) Lock前綴指令會引發處理器緩存回寫到內存。數據庫
(2) 一個處理器的緩存回寫到內存會致使其餘處理器的緩存失效。編程
三、鎖的狀態,級別從低到高爲:無鎖狀態,偏向鎖狀態,輕量級鎖狀態,重量級鎖狀態數組
鎖能夠升級但不能降級緩存
偏向鎖:只有一個線程進入臨界區安全
輕量級鎖:多個線程交替進入臨界區 CAS數據結構
重量級鎖:多個線程同時進入臨界區
四、處理器實現原子操做:(1) 總線鎖 (2) 緩存鎖定
五、Java實現原子操做:(1) 鎖 (2) 循環CAS
六、volatile:寫happens-before 讀
寫一個volatile變量時,JMM (Java內存模型) 會把該線程對應的本地內存中的共享變量刷新到主內存中。
讀一個volatile變量時,JMM會把本地內存置無效,從內存讀取共享變量。
七、CAS:先操做比較與預期的值是否同樣,同樣就設置,不同就繼續循環(CompareAndSet)
CAS同時具備volatile讀與寫到內存語義
八、happens-before 先行規則
(1) 程序順序規則:一個線程的每一個操做happens-before於該線程中的任意後序操做
(2) 監視器鎖規則:對一個鎖的解鎖happens-before於任意後序對這個鎖的加鎖
(3) volatole變量規則:對一個volatile域的寫happens-brfore於讀
(4) 傳遞性:A happens-before B,B happens-before C => A happens-before C
(5) start()規則:若是A執行 ThreadB.start(),那麼 A線程的ThreadB.start() happens-before B的任意操做
(6) join()規則:若是A執行ThreadB.join()併成功返回,那麼B中任意操做happens-before 於A從 ThreadB,join()操做成功返回
九、上下文切換:任務從保存到加載的過程
減小上下文切換有無鎖併發編程,CAS算法,使用最少線程和使用協程
十、Java內存模型
線程之間的共享變量存儲在主內存中,每一個內存都有一個私有的本地內存,本地內存存儲了該線程讀寫共享變量的副本,本地內存只是一抽象,並不真實存在。
十一、final 內存語義
(1) 在構造函數內對final 域的寫入,與隨後把這個被構造對象的引用賦值給一個引用變量,不能重排序。
(2) 初次讀一個包含final域對象的引用,與隨後初次讀這個fianl域,不能重排序。
十二、as-if-serial 語義保證單線程內程序的執行結果不被改變
happens-before 保證正確同步的多線程程序執行結果不被改變
1三、HashMap
jdk1.7多線程put會死循環,由於Entry鏈表造成環數據結構,Entry的next永遠不爲空
沒使用Synchronized,可接受 null 的 key 和 value
& 默認容量爲16,擴容2倍 自定義的hash
HashTable
使用 Synchronized,key 和value 都不能 null
% 默認容量爲11,擴容兩倍+1 hashcode
ConcurrentHashMap
段分鎖,提升併發訪問效率
segment->可重入鎖,2^n長度 包含HashEntry的鏈表結構
1四、ConcurrentHashMap
(1) 由 Segment 和 HashEntry 構成 , Segment長度爲2的n次方,jdk1.7 Segment會鎖住多個HashEntry
(2) get:先通過一次再散列,根據值使用散列運算定位到Segment,再經過散列算法定位到元素。get裏面的共享變量都定義爲volatile類型,因此不用加鎖
(3)定位HashEntry 和定位Segment 都是與數組長度-1 相與,可是相與的"值"不同。Segment使用的是元素的hashcode 經過再散列後的值的高位,定位HashEntry 直接使用再散列後的值。目的:避免兩次值同樣
(4) put 必須加鎖,定位到Segment ,判斷是否HashEntry 要擴容,而後再添加
(5) 第一次put時才初始化
1五、ConcurrentHashMap
jdk1.8改進:(1)取消Segment字段,使用volatile HashEntry<K,V>[] 數組元素做爲鎖 (2) 改成table數組 + 單向鏈表 + 紅黑樹,節點超過8會用紅黑樹
jdk1.7
put:(1) A tryLock 成功得到鎖,把hashEntry插入
(2)獲取鎖失敗,執行scanAndLockForPut方法,重複執行tryLock,多處理器重複64,單處理器重複1次,超過期,掛起線程
size:(1)不加鎖,計算兩次,相同說明準確。不同,給每一個Segment加鎖,再計算
modCount put,remove,clean時會加1
jdk1.8
size:元素個數保存在baseCount,部分元素的變化個數保存在CounterCell數組中,累加
a、新增節點後,鏈表的元素個數達到8,就轉換成紅黑樹,不過轉換以前,若是數組長度小於閥值,默認爲64 (數組長度大於64,纔會考慮轉換紅黑樹),則會擴大兩倍,並觸發transfer
b、元素個數0.75倍數組,擴容 (hashMap也同樣)
1六、ReentrantLock 可重入鎖,分爲公平鎖和非公平鎖
a、公平鎖:隊列順序,等待久的先
非公平鎖:能夠搶佔 CAS
b、公平鎖和非公平鎖,釋放最後都要寫一個volatile變量
公平鎖,獲取時會先讀volatile變量。非公平鎖獲取時,會用CAS更新volatile變量
c、非公平鎖用CAS。 公平鎖加個判讀去年當前節點是否有前驅節點 hasQueuedPredecessors()方法
d、非公平鎖可能形成線程"飢餓",可是極少的線程切換,保證吞吐量,吞吐量更大。
1七、ReetrantReadWriteLock讀寫鎖
高16位讀,低16位寫 AQS 可重入
鎖降級:把持當前擁有的寫鎖,再獲取讀鎖,稍後釋放鎖。目的:保證數據的可見性
1八、隊列同步器 AQS (AbstractQueuedSynchronizer) 構建鎖和或其餘同步組件的基礎架構 ReetrantLock,Condition,ReetrantReadWriteLock等
同步器提供 getState(),setState( .... ),conpareAndSetState( .... )
重寫AQS時,可使用CAS等
實現:a、同步隊列 雙向鏈表 ,CAS設置尾節點
b、獨佔式同步狀態獲取與釋放 CAS入隊enq(node),出隊時,每一個node都在自旋
c、共享式 <-- 釋放同步線程CAS
內部使用volatile修飾 int state 表示同步狀態
共享式同步狀態:tryAcquireShared(arg) >= 0時,能得到同步狀態
獲取同步狀態失敗進入同步隊列(addWaiter),先CAS設置,失敗進入enq方法。出隊列不須要CAS
1九、ConcurrentLinkedQueue 不要用size(),會遍歷所有
(1)非阻塞,入隊永遠返回true,用CAS
不是每次節點入隊後都將tail節點更新爲尾節點,而是當tail與尾節點距離>=HOPS常量時才更新,提升入隊效率
出隊僅沒有元素時纔會更新head節點,減小CAS
(2)阻塞隊列
插入:隊列滿時阻塞 移除:隊列空時阻塞
使用通知模式 condition 消費者消費通知生產者
阻塞生產者經過LockSupport.park實現
20、Condition接口 是AQS的內部類
condition定義了await()和signal(),signalAll() 方法
獲取一個condition要經過Lock的newCondition。
一個condition包含一個等待隊列,增長節點沒有CAS保證,由await()鎖保證線程安全
(1) await()時會使當前線程從同步隊列首節點構形成一個新節點,加入等待隊列中。
(2) 調用condition的signal() 方法時,將等待隊列中首節點加到同步隊列中
2一、LockSupport 每一個線程都有一個Park實例, unPark能夠先於Park出現
基於Unsafe實現的
使用Park使線程掛起,釋放(1) 其餘線程調用unPark,(2) 線程中斷,(3) park方法當即返回
2二、Fork/Join框架:分割任務,執行併合並結果。 工做竊取算法
2三、CycleBarrier 全部線程彼此等待 CountDownLatch:只要報到
容許一個或多個線程等待其餘線程完成操做
CountDownLatch的計數器只使用一次
CycleBarrier能夠reset(),跟join差很少(AQS,ReetrantLock)
等多有線程都運行到下一個步驟前等待
2四、控制併發線程數的Semaphore (信號量) AQS
用來控制同時訪問特定資源的線程數量
2五、Synchronize和Lock的區別
(1) Lock的鎖是Java寫的控制鎖的代碼。 Synchronize是託管給jvm的,Java關鍵字
(2) Synchronize在異常時,jvm會釋放鎖。 Lock不會主動釋放鎖,要手工釋放。
(3) Synchronize 悲觀的排他鎖。 Lock有讀寫鎖,公平鎖,非公平鎖
2六、線程池 ThreadPoolExecutor
好處:(1) 下降資源消耗 (2) 提升響應速度 (3) 提升線程可管理性
流程:a、判斷核心線程池線程是否執行任務。 建立線程須要獲取全局鎖
b、是的話,判斷工做隊列是否已滿。 目的:儘量避免獲取全局鎖
c、是的話,判斷線程池的線程是否都處於工做狀態
c滿了就調用飽和策略。默認拋出異常
提交任務:execute() 不返回值 submit() 返回future類型的對象
關閉池:shutdownNow 中止全部 shutdown 中斷沒有執行任務的線程
線程池最大容量 AtomicInteger類型,capacity前三位爲標誌位,因此最大爲(2^29) - 1
狀態分別爲:RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED
參數:corePoolSize 核心線程數 maximumPoolSize 最大線程數 keepAliveTime 最大存活時間 rejectExecutionHandler 任務拒絕處理策略
2七、Executoe框架
應用框架經過Executor框架控制上層調度,而下層調度由操做系統內核控制
使用流程:主線程先建立Runnable或Callable接口,而後交給ExecutorService執行,返回Future接口對象
2八、ThreadLocal
一個線程能夠根據ThreadLocal查詢到綁定在這個線程的一個值,可用於數據庫鏈接
提供get和set訪問與當前相關的局部變量。變量是保存在線程中的threadLocals (threadLocalMap類型的)
ThradLocalMap.Entry弱引用,隨時可能被回收
get先獲得當前線程的ThreadLocalMap,再根據 ××.Entry = get(this) 獲得值
2九、Synchroinzed:jvm基於進入與退出使用monitorenter和monitorexit指令實現,偏向鎖,輕量級鎖
30、atomic×××
value成員都是volatile CAS
基本方法:get/set,compareAndSet (unsafe.compareAnsSwapInt(....) )
CAS:調用UnSafe的方法,不是用Java實現,而是JNI調用操做系統原生程序
3一、CopyOnWriteArrayList 佔用內存
當咱們往一個容器添加元素時,不直接往當前容器添加,而是先copy複製一個新的容器,添加完後,再將原容器指向新的。
併發讀不用加鎖。添加時要加鎖,不然多線程會copy出多個副本。
若是在最後添加元素,則用Array.copyOf()。若是在中間插入,則用System.arraycopy分兩段複製。
應用讀多寫少併發場景,如白名單,黑名單
參考書籍:Java併發編程的藝術(推薦),juc包源碼
--------------------------------------------------------------------------------------------------------------
以上爲maplefighting我的筆記整理,若有出錯,歡迎指正