第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

JAVA 多線程鎖介紹

1. 前言

本節(jié)內(nèi)容主要是對 Java 多線程鎖進行介紹,是對鎖的一個全方位的概述,為我們對后續(xù)深入學(xué)習(xí)不同的鎖的使用方法奠定一個良好的基礎(chǔ)。本節(jié)內(nèi)容的知識點如下:

  • 樂觀鎖與悲觀鎖的概念,以及兩種鎖之間的區(qū)別,這是并發(fā)編程中經(jīng)常涉及到的知識點,這是本節(jié)課程的核心知識點,是熱度很高的必須要掌握的知識,后續(xù)還會有專門的小節(jié)進行詳細(xì)講解;
  • 公平鎖與非公平鎖的介紹,并發(fā)編程中經(jīng)常涉及到的知識點,需要掌握其概念與區(qū)別;
  • 獨占鎖與共享鎖的介紹,并發(fā)編程中經(jīng)常涉及到的知識點,需要掌握其概念與區(qū)別;
  • 自旋鎖的介紹,對于自旋鎖,了解其概念即可。

2. 悲觀鎖

定義:悲觀鎖指對數(shù)據(jù)被外界修改持保守態(tài)度,認(rèn)為數(shù)據(jù)很容易就會被其他線程修改(很悲觀),所以在數(shù)據(jù)被處理前先對數(shù)據(jù)進行加鎖,并在整個數(shù)據(jù)處理過程中,使數(shù)據(jù)處于鎖定狀態(tài)。

悲觀鎖的實現(xiàn):開發(fā)中常見的悲觀鎖實現(xiàn)往往依靠數(shù)據(jù)庫提供的鎖機制,即在數(shù)據(jù)庫中,在對數(shù)據(jù)記錄操作前給記錄加排它鎖。如果獲取鎖失敗,則說明數(shù)據(jù)正在被其他線程修改,當(dāng)前線程則等待或者拋出異常。如果獲取鎖成功,則對記錄進行操作,然后提交事務(wù)后釋放排它鎖。

實例:Java 中的 synchronized 關(guān)鍵字就是一種悲觀鎖,一個線程在操作時,其他的線程必須等待,直到鎖被釋放才可進入方法進行執(zhí)行,保證了線程和數(shù)據(jù)的安全性,同一時間,只能有一條線程進入執(zhí)行。

我們用一段熟悉的代碼進行悲觀鎖的展示。

public class Student {
    private String name;

    public synchronized String getName() {
        return name;
    }

    public synchronized void setName(String name) {
        this.name = name;
    }
}

代碼分析 :假設(shè)有 3 條線程,如下圖,線程 3 正在操作 Student 類,此時線程 1 和線程 2 必須要等待線程 3 執(zhí)行完畢方可進入,這就是悲觀鎖。

圖片描述

3. 樂觀鎖

定義:樂觀鎖是相對悲觀鎖來說的,它認(rèn)為數(shù)據(jù)在一般情況下不會造成沖突,所以在訪問記錄前不會加排它鎖,而是在進行數(shù)據(jù)提交更新的時候,才會正式對數(shù)據(jù)沖突與否進行檢測。

樂觀鎖的實現(xiàn):依舊拿數(shù)據(jù)庫的鎖進行比較介紹,樂觀鎖并不會使用數(shù)據(jù)庫提供的鎖機制, 一般在表中添加 version 宇段或者使用業(yè)務(wù)狀態(tài)來實現(xiàn)。 樂觀鎖直到提交時才鎖定,所以不會產(chǎn)生任何死鎖。

Java 中的樂觀鎖:我們之前所學(xué)習(xí)的 CAS 原理即是樂觀鎖技術(shù),當(dāng)多個線程嘗試使用 CAS 同時更新同一個變量時,只有其中一個線程能更新變量的值,而其它線程都失敗,失敗的線程并不會被掛起,而是被告知這次競爭中失敗,并可以再次嘗試。

圖片描述

Tips:我們這里所說的對于樂觀鎖,當(dāng)多個線程嘗試使用 CAS 同時更新同一個變量時,只有其中一個線程能更新變量的值,而其它線程都失敗。注意失敗兩字,失敗意味著有操作,而悲觀鎖是等待,意味著不能同時操作。

4. 悲觀鎖機制存在的問題

  • 在多線程競爭下,加鎖、釋放鎖會導(dǎo)致比較多的上下文切換和調(diào)度延時,引起性能問題;
  • 一個線程持有鎖會導(dǎo)致其它所有需要此鎖的線程掛起;
  • 如果一個優(yōu)先級高的線程等待一個優(yōu)先級低的線程釋放鎖會導(dǎo)致優(yōu)先級倒置,引起性能風(fēng)險。

對比于悲觀鎖的這些問題,另一個更加有效的鎖就是樂觀鎖。其實樂觀鎖就是:每次不加鎖而是假設(shè)沒有并發(fā)沖突而去完成某項操作,如果因為并發(fā)沖突失敗就重試,直到成功為止。

5. 公平鎖與非公平鎖

分類:根據(jù)線程獲取鎖的搶占機制,鎖可以分為公平鎖和非公平鎖。

公平鎖:表示線程獲取鎖的順序是按照線程請求鎖的時間早晚來決定的,也就是最早請求鎖的線程將最早獲取到鎖。

非公平鎖:非公平鎖則在運行時闖入,不遵循先到先執(zhí)行的規(guī)則。

ReentrantLock:ReentrantLock 提供了公平和非公平鎖的實現(xiàn)。我們本節(jié)只做介紹,后續(xù)章節(jié)會對 ReentrantLock 進行深入的講解。

6. 獨占鎖與共享鎖

分類:根據(jù)鎖只能被單個線程持有還是能被多個線程共同持有,鎖可以分為獨占鎖和共享鎖。

獨占鎖:保證任何時候都只有一個線程能得到鎖,ReentrantLock 就是以獨占鎖方式實現(xiàn)的。

共享鎖:則可以同時由多個線程持有,例如 ReadWriteLock 讀寫鎖,它允許一個資源可以被多線程同時進行讀操作。

獨占鎖是一種悲觀鎖,由于每次訪問資源都先加上互斥鎖,這限制了并發(fā)性,因為讀操作并不會影響數(shù)據(jù)的一致性,而獨占鎖只允許在同一時間由一個線程讀取數(shù)據(jù),其他線程必須等待當(dāng)前線程釋放鎖才能進行讀取。

共享鎖則是一種樂觀鎖,它放寬了加鎖的條件,允許多個線程同時進行讀操作。

7. 自旋鎖

由于 Java 中的線程是與操作系統(tǒng)中的線程相互對應(yīng)的,所以當(dāng)一個線程在獲取鎖(比如獨占鎖)失敗后,會被切換到內(nèi)核狀態(tài)而被掛起。

當(dāng)該線程獲取到鎖時又需要將其切換到內(nèi)核狀態(tài)而喚醒該線程。而從用戶狀態(tài)切換到內(nèi)核狀態(tài)的開銷是比較大的,在一定程度上會影響并發(fā)性能。

自旋鎖:自旋鎖則是當(dāng)前線程在獲取鎖時,如果發(fā)現(xiàn)鎖已經(jīng)被其他線程占有,它不馬上阻塞自己,在不放棄 CPU 使用權(quán)的情況下,多次嘗試獲取(默認(rèn)次數(shù)是 10,可以使用-XX:PreBlockSpinsh 參數(shù)設(shè)置該值)。

很有可能在后面幾次嘗試中其他線程己經(jīng)釋放了鎖。如果嘗試指定的次數(shù)后仍沒有獲取到鎖則當(dāng)前線程才會被阻塞掛起。由此看來自旋鎖是使用 CPU 時間換取線程阻塞與調(diào)度的開銷,但是很有可能這些 CPU 時間白白浪費了。

8. 小結(jié)

本節(jié)內(nèi)容為鎖的基礎(chǔ)概念介紹,其中對于悲觀鎖和樂觀鎖的講解,為本節(jié)核心知識點,因為這兩種鎖在數(shù)據(jù)庫的領(lǐng)域也是應(yīng)用十分廣泛。對于本節(jié)提到的具體的鎖實現(xiàn),如 ReentrantLock ,我們在后續(xù)章節(jié)中會有詳細(xì)的講解。

本節(jié)內(nèi)容為基礎(chǔ)介紹,掌握本節(jié)內(nèi)容的基礎(chǔ)上,能夠更好地學(xué)習(xí)后續(xù)的章節(jié)知識。