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

溫馨提示×

溫馨提示×

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

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

synchronized原理是什么

發布時間:2021-10-21 11:21:54 來源:億速云 閱讀:133 作者:iii 欄目:編程語言

本篇內容介紹了“synchronized原理是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

(一)概述

在多線程的程序執行中,有可能會出現多個線程會同時訪問一個共享并且可變資源的情況,這種時候由于線程的執行是不可控的,所以必須采用一些方式來控制該資源的訪問,這種方式就是“加鎖”。

我們把那些可能會被多個線程同時操作的資源稱為臨界資源,加鎖的目的就是讓這些臨界資源在同一時刻只能有一個線程可以訪問。

(二)CAS的介紹

CAS:compare and swap,比較且交換。使用CAS操作可以在沒有鎖的情況下完成多線程對一個值的更新。CAS的具體操作如下:

當要更新一個值時,先獲取當前值E,計算更新后的結果值V(先不更新),當要去更新這個值時,比較此時這個值是否還是等于E,如果相等,則將E更新為V,如果不相等,則重新進行上面的操作

以i++操作為例,在沒有鎖的情況下,這個操作是線程不安全的,假設i的初始值為0,CAS操作先獲取原值E=0,計算更新后的值V=1,要更新之前先比較這個值是否還是等于0,如果等于0則將E更新為1,如果不等于0則說明有線程已經更新了,重新獲取E值=1,繼續執行。

ABA問題

CAS操作可能會出現ABA問題,ABA問題即我們要去比較的這個值E,經過多個線程的操作后從0變成1又變成了0。此時雖然E值和更新前相等,但是還是已經被更新了。

ABA問題的解決辦法

對E值增加一個版本號,每次要獲取數據時將版本號也獲取,每次更新完數據之后將版本號遞增,這樣就算值相等通過版本號也能知道是否經過修改。

java在很多地方都用到了CAS操作,比如Atomic的一些類:

AtomicInteger i=new AtomicInteger();

進入AtomicInteger方法中,可以看到有個叫Unsafe的類,進入這個類中,可以看到CAS的幾個操作方法

synchronized原理是什么

(三)對象在內存中的存儲布局

要想學會synchronized,首先要理解Java對象的內存布局,或者稱為內存結構。

synchronized原理是什么

一個對象分為對象頭、實例數據和對其填充。

其中對象頭Header占12個字節:Mark Word占8個字節,類型指針class pointer占4個字節(默認經過了壓縮,如果不開啟壓縮占8個字節)

實例對象按實際存儲有不同大小,對象為空時等于0。

Padding表示對齊,當此時內存所占字節不能被8整除時補上相應字節數。

以Object o=new Object()為例,我們先導入一個jol依賴,通過jol可以看到具體的內存布局

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.9</version>
</dependency>

運行以下代碼:

public static void main(String[] args) {
    Object o=new Object();
    System.out.println(ClassLayout.parseInstance(o).toPrintable());
}

觀察結果,OFFSET表示偏移量的起始點,SIZE表示所占字節,前兩行是Mark Word一共占8個字節,第三行是class pointer占4個字節,此時對象為空,實例對象等于0,最后padding補齊,一共16個字節。

synchronized原理是什么

(三)synchronized

synchronized可以保證在同一時刻,只有一個線程可以執行某個方法或某個代碼塊,synchronized把鎖信息存放在對象頭的MarkWord中。

synchronized作用在非靜態方法上是對方法的加鎖,synchronized作用在靜態方法上是對當前的類加鎖。

在早期的jdk版本中,synchronized是一個重量級鎖,保證線程的安全但是效率很低。后來對synchronized進行了優化,有了一個鎖升級的過程

無鎖態(new)-->偏向鎖-->輕量級鎖(自旋鎖)-->重量級鎖

通過MarkWord中的8個字節也就是64位來記錄鎖信息。也有人將自旋鎖稱為無鎖,因為自選操作并沒有給一個對象上鎖,這里只要理解意思即可。

synchronized原理是什么

3.1 鎖升級過程詳解:

當給一個對象增加synchronized鎖之后,相當于上了一個偏向鎖

當有一個線程去請求時,就把這個對象MarkWord的ID改為當前線程指針ID(JavaThread),只允許這一個線程去請求對象。

當有其他線程也去請求時,就把鎖升級為輕量級鎖。每個線程在自己的線程棧中生成LockRecord,用CAS自旋操作將請求對象MarkWordID改為自己的LockRecord,成功的線程請求到了該對象,未成功的對象繼續自旋。

如果競爭加劇,當有線程自旋超過一定次數時(在JDK1.6之后,這個自旋次數由JVM自己控制),就將輕量級鎖升級為重量級鎖,線程掛起,進入等待隊列,等待操作系統的調度。

3.2 加鎖的字節碼實現

synchronized關鍵字被編譯成字節碼之后會被翻譯成monitorenter和monitorexit兩條指令,進入同步代碼塊時執行monitorenter,同步代碼塊執行完畢后執行monitorexit

(四)鎖消除

在某些情況下,如果JVM認為不需要鎖,會自動消除鎖,比如下面這段代碼:

public void add(String a,String b){
    StringBuffer sb=new StringBuffer();
    sb.append(a).append(b);
}

StringBuffer是線程安全的,但是在這個add方法中stringbuffer是不能共享的資源,因此加鎖只會徒增性能消耗,JVM就會消除StringBuffer內部的鎖。

(五)鎖粗化

在某些情況下,JVM檢測到一連串的操作都在對同一個對象不斷加鎖,就會將這個鎖加到這一連串操作的外部,比如:

StringBuffer sb=new StringBuffer();
while(i<100){
    sb.append(str);
    i++;
}

上述操作StringBuffer每次添加數據都要加鎖和解鎖,連續100次,這時候JVM就會將鎖加到更外層(while)部分。

(六)逃逸分析

首先問一個經常基礎的虛擬機問題,實例對象存放在虛擬機的哪個位置?按以前的回答,示例對象放在堆上,引用放在棧上,示例的元數據等存放在方法區或者元空間。

但這是有前提的,前提是示例對象沒有線程逃逸行為。

JDK1.7開始默認開啟了逃逸分析,所謂逃逸分析,就是指如果一個對象被編譯器發現只能被一個線程訪問,那么這個對象就不需要考慮同步。JVM就對這種對象進行優化,將堆分配轉化為棧分配,歸根結底就是虛擬機在編譯過程中對程序的一種優化行為。

開啟逃逸分析:- XX:+DoEscapeAnalysis
關閉逃逸分析: -XX:--DoEscapeAnalysis

“synchronized原理是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

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

AI

米泉市| 宁都县| 新源县| 昔阳县| 西平县| 镇赉县| 南漳县| 睢宁县| 洛浦县| 漳平市| 濉溪县| 禄劝| 乐清市| 黑山县| 定边县| 颍上县| 长顺县| 五家渠市| 长乐市| 大足县| 越西县| 米脂县| 舟曲县| 长治县| 弋阳县| 崇义县| 芜湖县| 蛟河市| 龙游县| 洪洞县| 庄河市| 万荣县| 镇巴县| 兴国县| 慈溪市| 莎车县| 邓州市| 兴安盟| 揭阳市| 左云县| 阳信县|