中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java 中的鎖是什么意思

發布時間:2021-07-12 11:49:51 來源:億速云 閱讀:166 作者:chen 欄目:編程語言

本篇內容主要講解“Java 中的鎖是什么意思”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Java 中的鎖是什么意思”吧!

鎖的類型

鎖的類型從不同的角度看,主要分為以下幾種

  • 悲觀鎖、樂觀鎖

  • 阻塞、非阻塞、自旋鎖

  • 公平、非公平

  • 可重入、不可重入

  • 共享鎖、排他鎖

悲觀鎖和樂觀鎖

悲觀鎖

  • 悲觀鎖認為自己在使用數據的時候一定有別的線程來修改數據,因此在獲取數據的時候會先加鎖,確保數據不會被別的線程修改。

  • 悲觀鎖適合寫操作多的場景,先加鎖可以保證寫操作時數據正確。

樂觀鎖

  • 樂觀鎖認為自己在使用數據時不會有別的線程修改數據,所以不會添加鎖,只是在更新數據的時候去判斷之前有沒有別的線程更新了這個數據。如果這個數據沒有被更新,當前線程將自己修改的數據成功寫入。

  • 樂觀鎖適合讀操作多的場景,不加鎖的特點能夠使其讀操作的性能大幅提升。

  • 樂觀鎖在java中通過使用無鎖編程來實現,原子類中的遞增操作就是通過CAS自旋來實現的。偽代碼如下:

do {
    value = getXXXVolatile()
} while(!compareAndSwapInt(obj, address, value, value + delta))

CAS

CAS全稱 Compare And Swap(比較與交換),是一種無鎖算法。在不使用鎖(沒有線程被阻塞)的情況下實現多線程之間的變量同步。

CAS是一個原子操作,底層通過Unsafe類進行操作。

阻塞、非阻塞、自旋鎖

阻塞或喚醒一個Java線程需要操作系統切換CPU狀態來完成,這種狀態轉換需要耗費處理器時間。如果同步代碼塊中的內容過于簡單,狀態轉換消耗的時間有可能比用戶代碼執行的時間還要長。

在許多場景中,同步資源的鎖定時間很短,為了這一小段時間去切換線程,線程掛起和恢復現場的花費可能會讓系統得不償失。如果物理機器有多個處理器,能夠讓兩個或以上的線程同時并行執行,我們就可以讓后面那個請求鎖的線程不放棄CPU的執行時間,看看持有鎖的線程是否很快就會釋放鎖。

而為了讓當前線程“稍等一下”,我們需讓當前線程進行自旋,如果在自旋完成后前面鎖定同步資源的線程已經釋放了鎖,那么當前線程就可以不必阻塞而是直接獲取同步資源,從而避免切換線程的開銷。這就是自旋鎖。

自旋鎖本身是有缺點的,它不能代替阻塞。自旋等待雖然避免了線程切換的開銷,但它要占用處理器時間。如果鎖被占用的時間很短,自旋等待的效果就會非常好。反之,如果鎖被占用的時間很長,那么自旋的線程只會白浪費處理器資源。所以,自旋等待的時間必須要有一定的限度,如果自旋超過了限定次數(默認是10次,可以使用-XX:PreBlockSpin來更改)沒有成功獲得鎖,就應當掛起線程。

自旋鎖的實現原理同樣也是CAS

公平鎖和非公平鎖

公平鎖是指多個線程按申請鎖的順序來獲取鎖,線程直接進入隊列中排隊,隊列中的第一個線程才能獲得鎖。

公平鎖的優點是等待鎖的線程不會餓死。缺點是整體吞吐效率相對非公平鎖要低,等待隊列中除第一個線程以外的所有線程都會阻塞,CPU喚醒阻塞線程的開銷比非公平鎖大。

非公平鎖是多個線程加鎖時直接嘗試獲取鎖,獲取不到才會到等待隊列的隊尾等待。但如果此時鎖剛好可用,那么這個線程可以無需阻塞直接獲取到鎖,所以非公平鎖有可能出現后申請鎖的線程先獲取鎖的場景。非公平鎖的優點是可以減少喚起線程的開銷,整體的吞吐效率高,因為線程有幾率不阻塞直接獲得鎖,CPU不必喚醒所有線程。缺點是處于等待隊列中的線程可能會餓死,或者等很久才會獲得鎖。

ReentrantLock支持公平鎖和非公平鎖兩種,默認是非公平鎖。

公平和非公平鎖內部都依賴AQS來實現。

AQS

AQS全程AbstractQueuedSynchronizer,即抽象同步隊列。內部主要包含以下重要組件:

  1. CLH 等待隊列,雙向鏈表,每個節點包含了Thread,nextWaiter和waitState,前驅節點和后驅節點

  2. state 同步狀態,初始值為0.

  3. 條件變量

公平和非公平的實現是放在AQS的兩個子類中實現的。

  • 公平鎖就是通過同步隊列來實現多個線程按照申請鎖的順序來獲取鎖,從而實現公平的特性。公平鎖在嘗試加鎖的時候,會檢測當前線程是否是CLH等待隊列中的第一個。

  • 非公平鎖加鎖時不考慮排隊等待問題,直接嘗試獲取鎖,所以存在后申請卻先獲得鎖的情況。

可重入鎖和非可重入鎖

可重入鎖又名遞歸鎖,是指在同一個線程在外層方法獲取鎖的時候,再進入該線程的內層方法會自動獲取鎖(前提鎖對象得是同一個對象或者class),不會因為之前已經獲取過還沒釋放而阻塞。Java中ReentrantLock和synchronized都是可重入鎖,可重入鎖的一個優點是可一定程度避免死鎖。

而非可重入鎖則限定一個線程內的同一個鎖,只能被一個資源獲取。

AQS邏輯

當線程嘗試獲取鎖時,可重入鎖先嘗試獲取并更新status值,如果status == 0表示沒有其他線程在執行同步代碼,則把status置為1,當前線程開始執行。如果status != 0,則判斷當前線程是否是獲取到這個鎖的線程,如果是的話執行status+1,且當前線程可以再次獲取鎖。而非可重入鎖是直接去獲取并嘗試更新當前status的值,如果status != 0的話會導致其獲取鎖失敗,當前線程阻塞。

排他鎖和共享鎖

共享鎖和排他鎖也稱讀寫鎖,分為讀鎖和寫鎖。

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

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

也是通過AQS來實現的,通過實現不同的方法,來實現獨享或者共享。

AQS實現

在jdk中讀寫鎖的實現類為ReentrantReadWriteLock,它內部有兩個鎖:ReadLock和WriteLock。讀鎖和寫鎖的鎖主體都是Sync,但讀鎖和寫鎖的加鎖方式不一樣。讀鎖是共享鎖,寫鎖是獨享鎖。讀鎖的共享鎖可保證并發讀非常高效,而讀寫、寫讀、寫寫的過程互斥,因為讀鎖和寫鎖是分離的。所以ReentrantReadWriteLock的并發性相比一般的互斥鎖有了很大提升。

在ReentrantReadWriteLock中,將state字段拆成了兩部分:高16位和低16位。高16位表示讀狀態,低16位表示寫狀態。

如果存在讀鎖,則寫鎖不能被獲取,原因在于:必須確保寫鎖的操作對讀鎖可見,如果允許讀鎖在已被獲取的情況下對寫鎖的獲取,那么正在運行的其他讀線程就無法感知到當前寫線程的操作。

因此,只有等待其他讀線程都釋放了讀鎖,寫鎖才能被當前線程獲取,而寫鎖一旦被獲取,則其他讀寫線程的后續訪問均被阻塞。寫鎖的釋放與ReentrantLock的釋放過程基本類似,每次釋放均減少寫狀態,當寫狀態為0時表示寫鎖已被釋放,然后等待的讀寫線程才能夠繼續訪問讀寫鎖,同時前次寫線程的修改對后續的讀寫線程可見。

到此,相信大家對“Java 中的鎖是什么意思”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

日照市| 红桥区| 赫章县| 明溪县| 武宣县| 罗源县| 浦东新区| 肥城市| 霞浦县| 赣榆县| 普陀区| 威信县| 万载县| 喀喇| 上犹县| 台南市| 邳州市| 宝兴县| 澎湖县| 林甸县| 太仓市| 德兴市| 民县| 彭州市| 琼结县| 同仁县| 金平| 永年县| 外汇| 甘德县| 措美县| 云浮市| 嘉鱼县| 鄂尔多斯市| 宾阳县| 蓬莱市| 高雄市| 资溪县| 潞城市| 文登市| 正安县|