Java併發包之閉鎖/柵欄/信號量及併發模型和鎖

threadLocal能夠爲每一個線程維護變量副本,常用於在多線程中用空間換時間

 

 

進程死鎖:進程死鎖,指多個進程循環等待他方佔有的資源而一直等待下去的局面;

 進程活鎖:線程1,2需要同時佔有a,b纔可以,1佔有a,2佔有b,爲了避免死鎖,1,2分別釋放,a,b空閒,此時1,2又同時搶鎖,發生活鎖;(電梯上遇到人,一個進一個出,2個人同時往一個方向讓路,來回反覆)

進程飢餓:指某一個或多個線程因爲各種原因無法獲取所需要的資源,導致一直無法執行;

 

一、樂觀鎖

 總是認爲不會產生併發問題,每次去取數據的時候總認爲不會有其他線程對數據進行修改,因此不會上鎖,但是在更新時會判斷其他線程在這之前有沒有對數據進行修改,一般會使用版本號機制或CAS操作實現。 樂觀鎖適用於多讀的應用類型,這樣可以提高吞吐量

 

 version方式:一般是在數據表中加上一個數據版本號version字段,表示數據被修改的次數,當數據被修改時,version值會加一。當線程A要更新數據值時,在讀取數據的同時也會讀取version值,在提交更新時,若剛纔讀取到的version值爲當前數據庫中的version值相等時才更新,否則重試更新操作,直到更新成功。

 

核心SQL代碼:

 

update table set x=x+1, version=version+1 where id=#{id} and version=#{version};  

 

 CAS操作方式:即compare and swap 或者 compare and set,涉及到三個操作數,數據所在的內存值,預期值,新值。當需要更新時,判斷當前內存值與之前取到的值是否相等,若相等,則用新值更新,若失敗則重試,一般情況下是一個自旋操作,即不斷的重試。

一、悲觀鎖

 總是假設最壞的情況,每次取數據時都認爲其他線程會修改,所以都會加鎖(讀鎖、寫鎖、行鎖等),當其他線程想要訪問數據時,都需要阻塞掛起。可以依靠數據庫實現,如行鎖、讀鎖和寫鎖等,都是在操作之前加鎖,在Java中,synchronized的思想也是悲觀鎖。 共享資源每次只給一個線程使用,其它線程阻塞,用完後再把資源轉讓給其它線程)。

 

對於資源競爭較少(線程衝突較輕)的情況,使用synchronized同步鎖進行線程阻塞和喚醒切換以及用戶態內核態間的切換操作額外浪費消耗cpu資源;而CAS基於硬件實現,不需要進入內核,不需要切換線程,操作自旋機率較少,因此可以獲得更高的性能。

對於資源競爭嚴重(線程衝突嚴重)的情況,CAS自旋的概率會比較大,從而浪費更多的CPU資源,效率低於synchronized。

  。synchronized的底層實現主要依靠 Lock-Free 的隊列,基本思路是 自旋後阻塞競爭切換後繼續競爭鎖稍微犧牲了公平性,但獲得了高吞吐量。在線程衝突較少的情況下,可以獲得和CAS類似的性能;而線程衝突嚴重的情況下,性能遠高於CAS。

    傳統的併發模型:每個I/O流都有一個新的線程管理;

    I/O多路複用:只有單個線程,通過跟蹤每個I/O流的狀態,來管理多個I/O流;

    (我們都redis-client在操作的時候,會產生具有不同事件類型的socket。在服務端,有一段I/O多路複用程序,將其置入隊列之中,然後,文件事件分派器,依次去隊列中取,轉發到不同的事件處理器中)

 

Java併發包之閉鎖/柵欄/信號量

Java併發包之閉鎖/柵欄/信號量

1.提供內存可見性和防止指令重排的volatile屬於jvm關鍵字

2.而java.util.concurrent包(J.U.C)中包含的是java併發編程中有用的一些工具類,包括幾個部分: 

  • locks部分:包含在java.util.concurrent.locks包中,提供顯式鎖(互斥鎖和速寫鎖)相關功能。
  • atomic部分:包含在java.util.concurrent.atomic包中,提供原子變量類相關的功能,是構建非阻塞算法的基礎。
  • executor部分:散落在java.util.concurrent包中,提供線程池相關的功能。
  • collections部分:散落在java.util.concurrent包中,提供併發容器相關功能。
  • tools部分:散落在java.util.concurrent包中,提供同步工具類,如信號量、閉鎖、柵欄等功能。 

 

1、Semaphore信號量:跟鎖機制存在一定的相似性,semaphore也是一種鎖機制,所不同的是,reentrantLock是隻允許一個線程獲得鎖,而信號量持有多個許可(permits),允許多個線程獲得許可並執行。可以用來控制同時訪問某個特定資源的操作數量,或者同時執行某個指定操作的數量。

2、CountDownLatch閉鎖:允許一個或多個線程一直等待,直到其他線程的操作執行完後再執行。CountDownLatch是通過一個計數器來實現的,計數器的初始值爲線程的數量。每當一個線程完成了自己的任務後,計數器的值就會減1。當計數器值到達0時,它表示所有的線程已經完成了任務,然後在閉鎖上等待的線程就可以恢復執行任務。

    主要方法: 

        1. CountDownLatch.await():將某個線程阻塞住,直到計數器count=0才恢復執行。 

        2. CountDownLatch.countDown():將計數器count減1。

3、CyclicBarrier柵欄:用於阻塞一組線程直到某個事件發生。所有線程必須同時到達柵欄位置才能繼續執行下一步操作,且能夠被重置以達到重複利用。而閉鎖是一次性對象,一旦進入終止狀態,就不能被重置。

 

     閉鎖用於一組線程等待(阻塞)一個外部事件的發生,這個事件發生之前這些線程阻塞,等待控制線程打開閉鎖,然後這些線程同時開始執行。閉鎖強調的是阻塞後的同時開始;

 

柵欄則是一組線程相互等待,直到所有線程都到達某一點時纔打開柵欄,然後線程可以繼續執行,也就是說控制線程先設置一個時間點,然後這些線程各自執行,執行完等待(阻塞),直到這組線程中的所有線程執行完,然後控制線程柵欄打開,這些線程同時繼續執行。柵欄強調的是各自執行完後的相互等待以及繼續執行。

 

信號量根據一個計數器控制一個結果的數量,條件滿足情況下才能進行增加和移除操作,否則進行操作的線程阻塞。

場景對比:

 

l  閉鎖場景:幾個人相約去公園遊玩,在家做好準備,約定在某一時刻同時出發去公園,準備工作進行的快的不能提前出門,到點出門。

 

l  柵欄場景:幾個人相約去公園遊玩,幾個人去到公園門口,要等全部到達公園門口後才一起進入公園。

 

l  信號量場景:幾個人相約去公園遊玩,等大家都到公園後,發現來的太遲了,公園遊客飽和,公園限制入場遊客的數量。遊客在門口等待,出來一人,再進入一人,只能一個一個進入。

 

 

緩存

頁面緩存經常用在CMS(content manage system)內存管理系統裏面。

數據緩存經常會用在頁面的具體數據裏面。

 

頁面緩存(smarty靜態化技術)

第一次從數據庫讀取,然後生成一個靜態頁面,以後所有的讀取,只加載這個靜態頁面就可以了‘’

數據緩存

由於一個頁面有好幾種需要從不同的緩存中讀取數據的模塊,所以不適合使用頁面緩存

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章