JUC簡介


JUC

一. 概述


  1. JUC指的是JDK1.5中提供的一套並發包及其子包:
    • java.util.concurrent
    • java.util.concurrent.lock
    • java.util.cncurrent.atomic
  2. 主要內容有:阻塞式隊列、並發映射、鎖、執行器服務、原子性操作。

二. 原子性操作


原子性操作實際上是保證了屬性的原子性,底層是基於CAS+volatile來實現的

Ⅰ. 關於CAS

👉CAS

Ⅱ.關於volatile

volatile是java中的關鍵字之一,是Java中提供的用於保證線程通信間的輕量級通信機制。

  1. 特性:
    1. 保證線程的可見性。一個線程對主內存的數據做了改變,其他線程能夠立即感知到這個改變。
    2. 對單個讀/寫具有原子性,但是復合操作除外,例如i++不保證線程的原子性。原子性指線程的執行過程不可拆分,換言之,線程在執行過程中不會中斷。加鎖就是為了保證原子性。
  2. 內存語義:
    1. 當寫一個volatile變量時,JMM會把該線程對應的本地內存中的共享變量值立即刷新到主內存中。
    2. 當讀一個volatile變量時,JMM會把該線程對應的本地內存設置為無效,直接從主內存中讀取共享變量
  3. 實施機制
    1. 禁止指令重排。指令沒有按照預定順序調用執行,而是在底層產生了所謂的優化,導致順序發生了改變。指令重排不能違背happen-before原則。
    2. 內存屏障

三. LOCK鎖


Ⅰ. 鎖一些概念

  1. 鎖的公平和非公平原則:

    公平鎖:鎖的獲取順序應該符合請求的絕對時間順序,也就是FIFO。

    非公平鎖:只要CAS設置同步狀態成功,則表示當前線程獲取了鎖

    1. 在資源有限的情況下,線程之間實際執行的次數並不均等,這種現象稱之為非公平原則。在公平策略下,線程不能直接搶占資源,而是搶占入隊順序。此時線程之間實際執行次數大致相等,我們稱之為公平策略。
    2. 相對而言,非公平的效率更高(不需要考慮調度問題)
  2. 鎖的獨占和共享

    獨占鎖:獨占鎖也叫排他鎖,是指該鎖一次只能被一個線程所持有。如果線程T對數據A加上排他鎖后,則其他線程不能再對A加任何類型的鎖。獲得排它鎖的線程即能讀數據又能修改數據。ReentrantLocksynchronized 都是獨占鎖

    共享鎖:享鎖是指該鎖可被多個線程所持有。如果線程T對數據A加上共享鎖后,則其他線程只能對A再加共享鎖,不能加排它鎖。獲得共享鎖的線程只能讀數據,不能修改數據。

    獨享鎖與共享鎖都是通過AQS來實現的,通過實現不同的方法,來實現獨享或者共享ReentrantReadWriteLock中讀鎖是共享鎖,寫鎖是獨占鎖。讀鎖的共享可以保證並發讀是高效的,讀寫,寫讀,寫寫是互斥的。

  3. 鎖的重入和非重入

    可重入鎖:可重入鎖也叫做遞歸鎖,指的是同一個線程T在進入外層函數A獲得鎖L之后,T繼續進入內層遞歸函數B,也需要獲取該鎖L的代碼時,在不釋放鎖L的情況下,可以重復獲取該鎖L。

    非重入鎖:非可重入鎖也叫做自旋鎖,對比上面,指的是同一個線程T在進入外層函數A獲得鎖L之后,T繼續進入內層遞歸函數B時,仍然有獲取該鎖L的代碼,必須要先釋放進入函數A的鎖L,才可以獲取進入函數B的鎖L。

  4. 鎖的樂觀和悲觀

    樂觀鎖:就像它的名字一樣,對於並發間操作產生的線程安全問題持樂觀狀態,樂觀鎖認為競爭不總是會發生,因此它不需要持有鎖,將樂觀鎖的核心算法是CAS,比較-替換這兩個動作作為一個原子操作嘗試去修改內存中的變量,如果失敗則表示發生沖突,那么就應該有相應的重試邏輯。(不加鎖就修改)

    悲觀鎖:還是像它的名字一樣,對於並發間操作產生的線程安全問題持悲觀狀態,悲觀鎖認為競爭總是會發生,因此每次對某資源進行操作時,都會持有一個獨占的鎖,就像synchronized,不管三七二十一,直接上了鎖就操作資源了。(加鎖才修改)

  5. 讀寫鎖

    讀鎖:當線程獲取讀鎖時,允許其他線程的讀操作,不允許寫操作。

    寫鎖:當線程獲取讀寫時,不允許其他線程的任何操作。

  6. 自旋

    很多synchronized里面的代碼只是一些很簡單的代碼,執行時間非常快,此時等待的線程都加鎖可能是一種不太值得的操作,因為線程阻塞涉及到用戶態和內核態切換的問題。既然synchronized里面的代碼執行得非常快,不妨讓等待鎖的線程不要被阻塞,而是在synchronized的邊界做忙循環,這就是自旋。如果做了多次忙循環發現還沒有獲得鎖,再阻塞,這樣可能是一種更好的策略。

Ⅱ. ReentrantLock

JDK1.5增加了LOCK鎖,可以通過顯示定義同步鎖對像實現同步,是對共享資源進行訪問的工具。相比synchronized,LOCK更加精細靈活。唯一實現類:ReentrantLock
【特點】

  1. 可重入。(Synchronized同)
  2. 如果不指定,默認非公平。(Synchronized同)
  3. 獨占(Synchronized同)
  4. 悲觀(Synchronized同)
  5. 底層采用AQS實現。

【案例】

import java.util.concurrent.locks.ReentrantLock;

/**
 * 銀行賬戶類
 * 此類為可變類,亦是線程不安全類。
 * 若想變為線程安全類,需付出額外的方法
 *
 * 此例中,需要將修改balance的方法同步
 * 若使用Synchronized同步,則鎖是this
 *
 * 此例也可顯示定義鎖對象,來同步方法
 * 注意要顯示的釋放鎖
 */
public class Account{
    private String accountNo;
    private double balance;
    //定義鎖對象
    private final ReentrantLock lock=new ReentrantLock();


    public void draw(double drawAmount){
        //加鎖
        lock.lock();
        try{
            if(drawAmount<balance){
                System.out.println("目前余額:"+balance);
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                balance-=drawAmount;
                System.out.println("余額:"+balance);
            }
            else {
                System.out.println("余額不足,提款失敗");
            }
        }finally {
            //修改完成,釋放鎖
            lock.unlock();
        }
    }
}

Ⅲ. ReadWriteLock

ReadWriteLock:讀寫鎖。在使用的時候先創建ReentrantReadWriteLock,通過這個對象獲取讀鎖或者寫鎖,之后再加鎖解鎖或者解鎖。

相比ReadWriteLock,ReentrantLock某些時候有局限。如果使用ReentrantLock,可能本身是為了防止線程A在寫數據、線程B在讀數據造成的數據不一致,但這樣,如果線程C在讀數據、線程D也在讀數據,讀數據是不會改變數據的,沒有必要加鎖,但是還是加鎖了,降低了程序的性能。

因為這個,才誕生了讀寫鎖ReadWriteLock。ReadWriteLock是一個讀寫鎖接口,ReentrantReadWriteLock是ReadWriteLock接口的一個具體實現,實現了讀寫的分離,讀鎖是共享的,寫鎖是獨占的,讀和讀之間不會互斥,讀和寫、寫和讀、寫和寫之間才會互斥,提升了讀寫的性能。

【關於StampedLock】

StampedLock是Java8新增的鎖,在絕大多數場景下可以替代傳統的讀寫鎖。其在提供讀寫鎖的同時,還支持優化讀模式。優化讀基於假設:大多數情況下讀操作並不會和寫操作沖突,所以可以先試着修改,然后通過validate方法確認是否進入了寫模式,如果沒有進入,就成功避免了開銷;如果進入了,則嘗試獲取讀鎖。

Ⅳ.Condition

ConditionObject是同步器AbstractQueuedSynchronizer的內部類 ,因為Condition的操作需要獲取相關聯的鎖,所以作為同步器的內部類也會是比較合理的。

每個Condition對象都包含着一個隊列(等待隊列),是Condition對象實現等待/通知功能的關鍵。

  • 等待隊列是一個FIFO隊列,隊列的每個節點都包含一個線程引用, 線程就是在Condition對象中等待的線程,如果一個線程調用了Condition.await()方法,那么該線程將會釋放鎖、構造節點加入等待隊列進入等待狀態。事實上,節點的定義復用了同步器中節點的定義,也就是說,同步隊列和等待隊列中節點類型都是同步器的經靜態內部類AbstractQueuedSynchronizer.Node

    一個Condition包含一個等待隊列,Condition擁有首節點(fristWaiter)和尾節點(lastWriter)。當前線程調用Condition.await()方法,將會以當前線程構造節點,並將節點從尾部加入等待隊列

  • 調用Condition的await()方法,會使當前線程進入等待隊列並釋放鎖,同時線程狀態變為等待狀態,當從await()方法返回時,當前線程一定獲取了Condition相關聯的鎖,

    如果從隊列 (同步隊列和等待隊列)的角度看await()方法,當調用await()方法時,相當於同步隊列的首節點(獲取了鎖的節點)移動到Condition的等待隊列中。

  • 調用Condition的signal()方法,將喚醒在等待隊列中等待時間最長的節點(首節點),在喚醒節點之前,會將節點移動到同步隊列中。

【案例】👉線程通信

Ⅴ. synchronized 和 ReentrantLock的區別

  1. synchronized是和if、else、for、while一樣的關鍵字,ReentrantLock是類,這是二者的本質區別。
  2. synchronized不需要顯示的定義鎖和釋放鎖。
  3. 既然ReentrantLock是類,那么它就提供了比synchronized更多更靈活的特性,可以被繼承、可以有方法、可以有各種各樣的類變量,ReentrantLock比synchronized的擴展性體現在幾點上:
    1. ReentrantLock可以對獲取鎖的等待時間進行超時設置,這樣就避免了死鎖。
    2. 等待可中斷:當持有鎖的線程長期不釋放鎖的時候,正在等待的線程可以選擇放棄等待,對處理執行時間非常長的同步塊很有用。
    3. 可以實現公平策略
    4. ReentrantLock可以獲取各種鎖的信息。
    5. ReentrantLock可以靈活地實現多路通知

四. BlockingQueue - 阻塞式隊列


Ⅰ. 特點

  1. 滿足隊列特點:FIFO(First In First Out)

  2. 阻塞:如果隊列為空,則試圖獲取元素的線程會被阻塞;如果隊列已滿,則試圖放入元素的線程會被阻塞。

  3. 不允許元素為null(LinkedList允許)

  4. 重要方法

    拋出異常 返回特殊值 永久阻塞 定時阻塞
    添加 add - IllegalStateException offer - false put offer
    獲取 remove - NoSuchElementException poll - null take poll

Ⅱ. 常用的實現類

  1. ArrayBlockingQueue阻塞式順序隊列
    1. 底層基於數組存儲數據
    2. 使用的時候需要指定容量,不能擴容
    3. 在多線程環境下不保證“公平性”
    4. 實現:ReentrantLock+Condition
  2. LinkedBlockingQueue阻塞式鎖式隊列
    1. 底層基於節點來存儲數據
    2. 在使用的時候可以指定容量也可以不指定。如果指定容量,則容量不可變;如果不指定容量,則容量默認為Integer.MAX_VALUE = 231-1不可變。因為實際開發中,一般不會在隊列中存儲21億個元素,所以一般認為此時的容量是無限的
  3. PriorityBlockingQueue具有優先級的阻塞式隊列:
    1. 底層基於節點來存儲數據
    2. 使用的時候可以指定容量也可以不指定。如果不指定則默認初始容量是11
    3. PriorityBlockingQueue會對放入的元素來進行排序,默認情況下元素采用自然順序升序排序,要求元素對應的類實現Comparable接口,覆蓋compareTo方法指定比較規則。
  4. SynchronousQueue 同步隊列
    1. 在使用的時候不需要指定容量,默認容量為1且只能為1
    2. 應用:交換工作,生產者的線程和消費者的線程同步以傳遞某些信息、事件或者任務

另:BlockingDeque阻塞式雙端隊列

  1. 允許從兩端放入/獲取元素。
  2. 遵循阻塞特點,在使用的時候需要指定容量。

五. 並發映射

六. 執行器服務


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM