java併發複習筆記總結

本文地址: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我的筆記整理,若有出錯,歡迎指正

相關文章
相關標籤/搜索