成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Synchronized 關(guān)鍵字使用、底層原理、JDK1.6 之后的底層優(yōu)化以及 和ReenTran

Vixb / 3020人閱讀

摘要:使用可以禁止的指令重排,保證在多線程環(huán)境下也能正常運行。關(guān)鍵字底層原理總結(jié)關(guān)鍵字底層原理屬于層面。另外在中引入了自適應(yīng)的自旋鎖。自適應(yīng)的自旋鎖帶來的改進就是自旋的時間不在固定了,而是和前一次同一個鎖上的自旋時間以及鎖的擁有者

【強烈推薦!非廣告!】阿里云雙11褥羊毛活動:https://m.aliyun.com/act/team1111/#/share?params=N.FF7yxCciiM.hf47liqn 差不多一折,不過僅限阿里云新人購買,不是新人的朋友自己找方法買哦!

Github 地址:https://github.com/Snailclimb/JavaGuide/edit/master/Java相關(guān)/synchronized.md

synchronized關(guān)鍵字最主要的三種使用方式的總結(jié)

修飾實例方法,作用于當前對象實例加鎖,進入同步代碼前要獲得當前對象實例的鎖

修飾靜態(tài)方法,作用于當前類對象加鎖,進入同步代碼前要獲得當前類對象的鎖 。也就是給當前類加鎖,會作用于類的所有對象實例,因為靜態(tài)成員不屬于任何一個實例對象,是類成員( static 表明這是該類的一個靜態(tài)資源,不管new了多少個對象,只有一份,所以對該類的所有對象都加了鎖)。所以如果一個線程A調(diào)用一個實例對象的非靜態(tài) synchronized 方法,而線程B需要調(diào)用這個實例對象所屬類的靜態(tài) synchronized 方法,是允許的,不會發(fā)生互斥現(xiàn)象,因為訪問靜態(tài) synchronized 方法占用的鎖是當前類的鎖,而訪問非靜態(tài) synchronized 方法占用的鎖是當前實例對象鎖。

修飾代碼塊,指定加鎖對象,對給定對象加鎖,進入同步代碼庫前要獲得給定對象的鎖。 和 synchronized 方法一樣,synchronized(this)代碼塊也是鎖定當前對象的。synchronized 關(guān)鍵字加到 static 靜態(tài)方法和 synchronized(class)代碼塊上都是是給 Class 類上鎖。這里再提一下:synchronized關(guān)鍵字加到非 static 靜態(tài)方法上是給對象實例上鎖。另外需要注意的是:盡量不要使用 synchronized(String a) 因為JVM中,字符串常量池具有緩沖功能!

下面我已一個常見的面試題為例講解一下 synchronized 關(guān)鍵字的具體使用。

面試中面試官經(jīng)常會說:“單例模式了解嗎?來給我手寫一下!給我解釋一下雙重檢驗鎖方式實現(xiàn)單利模式的原理唄!”

雙重校驗鎖實現(xiàn)對象單例(線程安全)

public class Singleton {

    private volatile static Singleton uniqueInstance;

    private Singleton() {
    }

    public static Singleton getUniqueInstance() {
       //先判斷對象是否已經(jīng)實例過,沒有實例化過才進入加鎖代碼
        if (uniqueInstance == null) {
            //類對象加鎖
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

另外,需要注意 uniqueInstance 采用 volatile 關(guān)鍵字修飾也是很有必要。

uniqueInstance 采用 volatile 關(guān)鍵字修飾也是很有必要的, uniqueInstance = new Singleton(); 這段代碼其實是分為三步執(zhí)行:

為 uniqueInstance 分配內(nèi)存空間

初始化 uniqueInstance

將 uniqueInstance 指向分配的內(nèi)存地址

但是由于 JVM 具有指令重排的特性,執(zhí)行順序有可能變成 1->3->2。指令重排在單線程環(huán)境下不會出先問題,但是在多線程環(huán)境下會導(dǎo)致一個線程獲得還沒有初始化的實例。例如,線程 T1 執(zhí)行了 1 和 3,此時 T2 調(diào)用 getUniqueInstance() 后發(fā)現(xiàn) uniqueInstance 不為空,因此返回 uniqueInstance,但此時 uniqueInstance 還未被初始化。

使用 volatile 可以禁止 JVM 的指令重排,保證在多線程環(huán)境下也能正常運行。

synchronized 關(guān)鍵字底層原理總結(jié)

synchronized 關(guān)鍵字底層原理屬于 JVM 層面。

① synchronized 同步語句塊的情況

public class SynchronizedDemo {
    public void method() {
        synchronized (this) {
            System.out.println("synchronized 代碼塊");
        }
    }
}

通過 JDK 自帶的 javap 命令查看 SynchronizedDemo 類的相關(guān)字節(jié)碼信息:首先切換到類的對應(yīng)目錄執(zhí)行 javac SynchronizedDemo.java 命令生成編譯后的 .class 文件,然后執(zhí)行javap -c -s -v -l SynchronizedDemo.class。

從上面我們可以看出:

synchronized 同步語句塊的實現(xiàn)使用的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代碼塊的開始位置,monitorexit 指令則指明同步代碼塊的結(jié)束位置。 當執(zhí)行 monitorenter 指令時,線程試圖獲取鎖也就是獲取 monitor(monitor對象存在于每個Java對象的對象頭中,synchronized 鎖便是通過這種方式獲取鎖的,也是為什么Java中任意對象可以作為鎖的原因) 的持有權(quán).當計數(shù)器為0則可以成功獲取,獲取后將鎖計數(shù)器設(shè)為1也就是加1。相應(yīng)的在執(zhí)行 monitorexit 指令后,將鎖計數(shù)器設(shè)為0,表明鎖被釋放。如果獲取對象鎖失敗,那當前線程就要阻塞等待,直到鎖被另外一個線程釋放為止。

② synchronized 修飾方法的的情況

public class SynchronizedDemo2 {
    public synchronized void method() {
        System.out.println("synchronized 方法");
    }
}

synchronized 修飾的方法并沒有 monitorenter 指令和 monitorexit 指令,取得代之的確實是 ACC_SYNCHRONIZED 標識,該標識指明了該方法是一個同步方法,JVM 通過該 ACC_SYNCHRONIZED 訪問標志來辨別一個方法是否聲明為同步方法,從而執(zhí)行相應(yīng)的同步調(diào)用。

在 Java 早期版本中,synchronized 屬于重量級鎖,效率低下,因為監(jiān)視器鎖(monitor)是依賴于底層的操作系統(tǒng)的 Mutex Lock 來實現(xiàn)的,Java 的線程是映射到操作系統(tǒng)的原生線程之上的。如果要掛起或者喚醒一個線程,都需要操作系統(tǒng)幫忙完成,而操作系統(tǒng)實現(xiàn)線程之間的切換時需要從用戶態(tài)轉(zhuǎn)換到內(nèi)核態(tài),這個狀態(tài)之間的轉(zhuǎn)換需要相對比較長的時間,時間成本相對較高,這也是為什么早期的 synchronized 效率低的原因。慶幸的是在 Java 6 之后 Java 官方對從 JVM 層面對synchronized 較大優(yōu)化,所以現(xiàn)在的 synchronized 鎖效率也優(yōu)化得很不錯了。JDK1.6對鎖的實現(xiàn)引入了大量的優(yōu)化,如自旋鎖、適應(yīng)性自旋鎖、鎖消除、鎖粗化、偏向鎖、輕量級鎖等技術(shù)來減少鎖操作的開銷。

JDK1.6 之后的底層優(yōu)化

JDK1.6 對鎖的實現(xiàn)引入了大量的優(yōu)化,如偏向鎖、輕量級鎖、自旋鎖、適應(yīng)性自旋鎖、鎖消除、鎖粗化等技術(shù)來減少鎖操作的開銷。

鎖主要存在四中狀態(tài),依次是:無鎖狀態(tài)、偏向鎖狀態(tài)、輕量級鎖狀態(tài)、重量級鎖狀態(tài),他們會隨著競爭的激烈而逐漸升級。注意鎖可以升級不可降級,這種策略是為了提高獲得鎖和釋放鎖的效率。

①偏向鎖

引入偏向鎖的目的和引入輕量級鎖的目的很像,他們都是為了沒有多線程競爭的前提下,減少傳統(tǒng)的重量級鎖使用操作系統(tǒng)互斥量產(chǎn)生的性能消耗。但是不同是:輕量級鎖在無競爭的情況下使用 CAS 操作去代替使用互斥量。而偏向鎖在無競爭的情況下會把整個同步都消除掉

偏向鎖的“偏”就是偏心的偏,它的意思是會偏向于第一個獲得它的線程,如果在接下來的執(zhí)行中,該鎖沒有被其他線程獲取,那么持有偏向鎖的線程就不需要進行同步!關(guān)于偏向鎖的原理可以查看《深入理解Java虛擬機:JVM高級特性與最佳實踐》第二版的13章第三節(jié)鎖優(yōu)化。

但是對于鎖競爭比較激烈的場合,偏向鎖就失效了,因為這樣場合極有可能每次申請鎖的線程都是不相同的,因此這種場合下不應(yīng)該使用偏向鎖,否則會得不償失,需要注意的是,偏向鎖失敗后,并不會立即膨脹為重量級鎖,而是先升級為輕量級鎖。

② 輕量級鎖

倘若偏向鎖失敗,虛擬機并不會立即升級為重量級鎖,它還會嘗試使用一種稱為輕量級鎖的優(yōu)化手段(1.6之后加入的)。輕量級鎖不是為了代替重量級鎖,它的本意是在沒有多線程競爭的前提下,減少傳統(tǒng)的重量級鎖使用操作系統(tǒng)互斥量產(chǎn)生的性能消耗,因為使用輕量級鎖時,不需要申請互斥量。另外,輕量級鎖的加鎖和解鎖都用到了CAS操作。 關(guān)于輕量級鎖的加鎖和解鎖的原理可以查看《深入理解Java虛擬機:JVM高級特性與最佳實踐》第二版的13章第三節(jié)鎖優(yōu)化。

輕量級鎖能夠提升程序同步性能的依據(jù)是“對于絕大部分鎖,在整個同步周期內(nèi)都是不存在競爭的”,這是一個經(jīng)驗數(shù)據(jù)。如果沒有競爭,輕量級鎖使用 CAS 操作避免了使用互斥操作的開銷。但如果存在鎖競爭,除了互斥量開銷外,還會額外發(fā)生CAS操作,因此在有鎖競爭的情況下,輕量級鎖比傳統(tǒng)的重量級鎖更慢!如果鎖競爭激烈,那么輕量級將很快膨脹為重量級鎖!

③ 自旋鎖和自適應(yīng)自旋

輕量級鎖失敗后,虛擬機為了避免線程真實地在操作系統(tǒng)層面掛起,還會進行一項稱為自旋鎖的優(yōu)化手段。

互斥同步對性能最大的影響就是阻塞的實現(xiàn),因為掛起線程/恢復(fù)線程的操作都需要轉(zhuǎn)入內(nèi)核態(tài)中完成(用戶態(tài)轉(zhuǎn)換到內(nèi)核態(tài)會耗費時間)。

一般線程持有鎖的時間都不是太長,所以僅僅為了這一點時間去掛起線程/恢復(fù)線程是得不償失的。 所以,虛擬機的開發(fā)團隊就這樣去考慮:“我們能不能讓后面來的請求獲取鎖的線程等待一會而不被掛起呢?看看持有鎖的線程是否很快就會釋放鎖”。為了讓一個線程等待,我們只需要讓線程執(zhí)行一個忙循環(huán)(自旋),這項技術(shù)就叫做自旋

百度百科對自旋鎖的解釋:

何謂自旋鎖?它是為實現(xiàn)保護共享資源而提出一種鎖機制。其實,自旋鎖與互斥鎖比較類似,它們都是為了解決對某項資源的互斥使用。無論是互斥鎖,還是自旋鎖,在任何時刻,最多只能有一個保持者,也就說,在任何時刻最多只能有一個執(zhí)行單元獲得鎖。但是兩者在調(diào)度機制上略有不同。對于互斥鎖,如果資源已經(jīng)被占用,資源申請者只能進入睡眠狀態(tài)。但是自旋鎖不會引起調(diào)用者睡眠,如果自旋鎖已經(jīng)被別的執(zhí)行單元保持,調(diào)用者就一直循環(huán)在那里看是否該自旋鎖的保持者已經(jīng)釋放了鎖,"自旋"一詞就是因此而得名。

自旋鎖在 JDK1.6 之前其實就已經(jīng)引入了,不過是默認關(guān)閉的,需要通過--XX:+UseSpinning參數(shù)來開啟。JDK1.6及1.6之后,就改為默認開啟的了。需要注意的是:自旋等待不能完全替代阻塞,因為它還是要占用處理器時間。如果鎖被占用的時間短,那么效果當然就很好了!反之,相反!自旋等待的時間必須要有限度。如果自旋超過了限定次數(shù)任然沒有獲得鎖,就應(yīng)該掛起線程。自旋次數(shù)的默認值是10次,用戶可以修改--XX:PreBlockSpin來更改。

另外,在 JDK1.6 中引入了自適應(yīng)的自旋鎖。自適應(yīng)的自旋鎖帶來的改進就是:自旋的時間不在固定了,而是和前一次同一個鎖上的自旋時間以及鎖的擁有者的狀態(tài)來決定,虛擬機變得越來越“聰明”了。

④ 鎖消除

鎖消除理解起來很簡單,它指的就是虛擬機即使編譯器在運行時,如果檢測到那些共享數(shù)據(jù)不可能存在競爭,那么就執(zhí)行鎖消除。鎖消除可以節(jié)省毫無意義的請求鎖的時間。

⑤ 鎖粗化

原則上,我們再編寫代碼的時候,總是推薦將同步快的作用范圍限制得盡量小——只在共享數(shù)據(jù)的實際作用域才進行同步,這樣是為了使得需要同步的操作數(shù)量盡可能變小,如果存在鎖競爭,那等待線程也能盡快拿到鎖。

大部分情況下,上面的原則都是沒有問題的,但是如果一系列的連續(xù)操作都對同一個對象反復(fù)加鎖和解鎖,那么會帶來很多不必要的性能消耗。

Synchronized 和 ReenTrantLock 的對比

① 兩者都是可重入鎖

兩者都是可重入鎖?!翱芍厝腈i”概念是:自己可以再次獲取自己的內(nèi)部鎖。比如一個線程獲得了某個對象的鎖,此時這個對象鎖還沒有釋放,當其再次想要獲取這個對象的鎖的時候還是可以獲取的,如果不可鎖重入的話,就會造成死鎖。同一個線程每次獲取鎖,鎖的計數(shù)器都自增1,所以要等到鎖的計數(shù)器下降為0時才能釋放鎖。

② synchronized 依賴于 JVM 而 ReenTrantLock 依賴于 API

synchronized 是依賴于 JVM 實現(xiàn)的,前面我們也講到了 虛擬機團隊在 JDK1.6 為 synchronized 關(guān)鍵字進行了很多優(yōu)化,但是這些優(yōu)化都是在虛擬機層面實現(xiàn)的,并沒有直接暴露給我們。ReenTrantLock 是 JDK 層面實現(xiàn)的(也就是 API 層面,需要 lock() 和 unlock 方法配合 try/finally 語句塊來完成),所以我們可以通過查看它的源代碼,來看它是如何實現(xiàn)的。

③ ReenTrantLock 比 synchronized 增加了一些高級功能

相比synchronized,ReenTrantLock增加了一些高級功能。主要來說主要有三點:①等待可中斷;②可實現(xiàn)公平鎖;③可實現(xiàn)選擇性通知(鎖可以綁定多個條件)

ReenTrantLock提供了一種能夠中斷等待鎖的線程的機制,通過lock.lockInterruptibly()來實現(xiàn)這個機制。也就是說正在等待的線程可以選擇放棄等待,改為處理其他事情。

ReenTrantLock可以指定是公平鎖還是非公平鎖。而synchronized只能是非公平鎖。所謂的公平鎖就是先等待的線程先獲得鎖。 ReenTrantLock默認情況是非公平的,可以通過 ReenTrantLock類的ReentrantLock(boolean fair)構(gòu)造方法來制定是否是公平的。

synchronized關(guān)鍵字與wait()和notify/notifyAll()方法相結(jié)合可以實現(xiàn)等待/通知機制,ReentrantLock類當然也可以實現(xiàn),但是需要借助于Condition接口與newCondition() 方法。Condition是JDK1.5之后才有的,它具有很好的靈活性,比如可以實現(xiàn)多路通知功能也就是在一個Lock對象中可以創(chuàng)建多個Condition實例(即對象監(jiān)視器),線程對象可以注冊在指定的Condition中,從而可以有選擇性的進行線程通知,在調(diào)度線程上更加靈活。 在使用notify/notifyAll()方法進行通知時,被通知的線程是由 JVM 選擇的,用ReentrantLock類結(jié)合Condition實例可以實現(xiàn)“選擇性通知” ,這個功能非常重要,而且是Condition接口默認提供的。而synchronized關(guān)鍵字就相當于整個Lock對象中只有一個Condition實例,所有的線程都注冊在它一個身上。如果執(zhí)行notifyAll()方法的話就會通知所有處于等待狀態(tài)的線程這樣會造成很大的效率問題,而Condition實例的signalAll()方法 只會喚醒注冊在該Condition實例中的所有等待線程。

如果你想使用上述功能,那么選擇ReenTrantLock是一個不錯的選擇。

④ 性能已不是選擇標準

在JDK1.6之前,synchronized 的性能是比 ReenTrantLock 差很多。具體表示為:synchronized 關(guān)鍵字吞吐量歲線程數(shù)的增加,下降得非常嚴重。而ReenTrantLock 基本保持一個比較穩(wěn)定的水平。我覺得這也側(cè)面反映了, synchronized 關(guān)鍵字還有非常大的優(yōu)化余地。后續(xù)的技術(shù)發(fā)展也證明了這一點,我們上面也講了在 JDK1.6 之后 JVM 團隊對 synchronized 關(guān)鍵字做了很多優(yōu)化。JDK1.6 之后,synchronized 和 ReenTrantLock 的性能基本是持平了。所以網(wǎng)上那些說因為性能才選擇 ReenTrantLock 的文章都是錯的!JDK1.6之后,性能已經(jīng)不是選擇synchronized和ReenTrantLock的影響因素了!而且虛擬機在未來的性能改進中會更偏向于原生的synchronized,所以還是提倡在synchronized能滿足你的需求的情況下,優(yōu)先考慮使用synchronized關(guān)鍵字來進行同步!優(yōu)化后的synchronized和ReenTrantLock一樣,在很多地方都是用到了CAS操作。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/71877.html

相關(guān)文章

  • 值得保存 synchronized 關(guān)鍵字總結(jié)

    摘要:無論是互斥鎖,還是自旋鎖,在任何時刻,最多只能有一個保持者,也就說,在任何時刻最多只能有一個執(zhí)行單元獲得鎖。另外在中引入了自適應(yīng)的自旋鎖。和關(guān)鍵字的總結(jié)推薦一 該文已加入開源文檔:JavaGuide(一份涵蓋大部分Java程序員所需要掌握的核心知識)。地址:https://github.com/Snailclimb... 本文是對 synchronized 關(guān)鍵字使用、底層原理、JD...

    miguel.jiang 評論0 收藏0
  • BATJ都愛問多線程面試題

    摘要:今天給大家總結(jié)一下,面試中出鏡率很高的幾個多線程面試題,希望對大家學(xué)習(xí)和面試都能有所幫助。指令重排在單線程環(huán)境下不會出先問題,但是在多線程環(huán)境下會導(dǎo)致一個線程獲得還沒有初始化的實例。使用可以禁止的指令重排,保證在多線程環(huán)境下也能正常運行。 下面最近發(fā)的一些并發(fā)編程的文章匯總,通過閱讀這些文章大家再看大廠面試中的并發(fā)編程問題就沒有那么頭疼了。今天給大家總結(jié)一下,面試中出鏡率很高的幾個多線...

    高勝山 評論0 收藏0
  • 這幾道Java集合框架面試題在面試中幾乎必問

    摘要:若遇到哈希沖突,則將沖突的值加到鏈表中即可。之后相比于之前的版本,之后在解決哈希沖突時有了較大的變化,當鏈表長度大于閾值默認為時,將鏈表轉(zhuǎn)化為紅黑樹,以減少搜索時間。有序,唯一紅黑樹自平衡的排序二叉樹。 本文是最最最常見Java面試題總結(jié)系列第三周的文章。主要內(nèi)容: Arraylist 與 LinkedList 異同 ArrayList 與 Vector 區(qū)別 HashMap的底層...

    bigdevil_s 評論0 收藏0
  • Java鎖機制

    摘要:它倆是不沖突的,也就是說獲取了類鎖的線程和獲取了對象鎖的線程是不沖突的可重入鎖住了鎖住了當線程進入到的方法時,此時拿到了實例對象的鎖。當一個線程執(zhí)行的代碼出現(xiàn)異常時,其所持有的鎖會自動釋放。 Java鎖機制 synchronized鎖 synchronized 簡介 synchronized是Java的一個關(guān)鍵字,它能夠?qū)⒋a塊(方法)鎖起來 synchronized是一種互...

    FreeZinG 評論0 收藏0
  • Java-Mysql你所需要面試題集內(nèi)容

    摘要:注意排版不需要花花綠綠的,盡量使用語法。協(xié)議的長連接和短連接,實質(zhì)上是協(xié)議的長連接和短連接。長連接短連接究竟是什么三次握手和四次揮手面試??蜑榱藴蚀_無誤地把數(shù)據(jù)送達目標處,協(xié)議采用了三次握手策略。 一 簡歷該如何寫 1.1 為什么說簡歷很重要?1.2-這3點你必須知道1.3-兩大法則了解一1.4-項目經(jīng)歷怎么寫?1.5-專業(yè)技能該怎么寫?1.6-開源程序員簡歷模板分享1.7 其他的一些...

    OpenDigg 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<