欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java同步鎖synchronized用法的最全總結(jié)

 更新時間:2023年03月21日 16:57:25   作者:老鼠只愛大米  
這篇文章主要介紹了Java同步鎖synchronized用法的最全總結(jié),需要的朋友可以參考下,文章詳細(xì)講解了Java同步鎖Synchronized的使用方法和需要注意的點,希望對你有所幫助

一、并發(fā)同步問題

  線程安全是Java并發(fā)編程中的重點,而造成線程安全問題的主要原因有兩點,一是存在共享數(shù)據(jù)(也稱臨界資源),二是存在多條線程共同操作共享數(shù)據(jù)。因此,當(dāng)存在多個線程操作共享數(shù)據(jù)時,需要保證同一時刻有且只有一個線程在操作共享數(shù)據(jù),其他線程必須等到該線程處理完數(shù)據(jù)后再進行,這種方式就叫互斥鎖。也就是說當(dāng)一個共享數(shù)據(jù)被正在訪問的線程加上互斥鎖后,在同一個時刻,其他線程只能處于等待的狀態(tài),直到當(dāng)前線程處理完畢釋放該鎖。在 Java 中,關(guān)鍵字 synchronized可以保證在同一個時刻,只有一個線程可以執(zhí)行某個方法或者某個代碼塊(主要是對方法或者代碼塊中存在共享數(shù)據(jù)的操作),同時synchronized還有另外一個重要的作用,它可以可保證一個線程的變化(主要是共享數(shù)據(jù)的變化)被其他線程所看到(保證可見性,完全可以替代Volatile功能)。

二、鎖的簡介

synchronized是Java的關(guān)鍵字,是一種同步鎖。
  Java的內(nèi)置鎖:每個java對象都可以用做一個實現(xiàn)同步的鎖,這些鎖稱為內(nèi)置鎖。線程進入同步代碼塊或方法的時候會自動獲得該鎖,在退出同步代碼塊或方法時會釋放該鎖。獲得內(nèi)置鎖的唯一途徑就是進入這個鎖的保護的同步代碼塊或方法。
  Java內(nèi)置鎖是一個互斥鎖,這就是意味著最多只有一個線程能夠獲得該鎖,當(dāng)線程A嘗試去獲得線程B持有的內(nèi)置鎖時,線程A必須等待或者阻塞,直到線程B釋放這個鎖。
  Java的對象鎖和類鎖:java的對象鎖和類鎖在鎖的概念上基本上和內(nèi)置鎖是一致的,但是兩個鎖實際是有很大的區(qū)別的,對象鎖是用于對象實例方法,或者一個對象實例上的,類鎖是用于類的靜態(tài)方法或者一個類的class對象上的。
  在Java中,每個對象都有一把鎖和兩個隊列,一個隊列用于掛起未獲得鎖的線程,一個隊列用于掛起條件不滿足而等待的線程。而synchronized實際上也就是一個加鎖和釋放鎖的集成。JVM負(fù)責(zé)跟蹤對象被加鎖的次數(shù)。如果一個對象被解鎖,其計數(shù)變?yōu)?。在任務(wù)(線程)第一次給對象加鎖的時候,計數(shù)變?yōu)?。每當(dāng)這個相同的任務(wù)(線程)在此對象上獲得鎖時,計數(shù)會遞增。只有首先獲得鎖的任務(wù)(線程)才能繼續(xù)多次獲取該對象上的鎖。每當(dāng)任務(wù)離開一個synchronized方法,計數(shù)遞減,當(dāng)計數(shù)為0的時候,鎖被完全釋放,此時別的任務(wù)就可以使用此資源。

三、synchronized的三種應(yīng)用方式

synchronized可以修飾范圍的包括:方法級別,代碼塊級別;而實際加鎖的目標(biāo)包括:對象鎖(普通變量,靜態(tài)變量),類鎖。具體分為三種應(yīng)用方式:

1.修飾一個實例方法

  被修飾的方法稱為實例同步方法,其作用范圍是整個方法,鎖定的是該方法所屬的對象(即調(diào)用該方法的對象)。所有需要獲得該對象鎖的操作都會對該對象加鎖(即訪問該對象的其他同步實例方法或進入對該對象加鎖的代碼塊)。實例同步方法的代碼如下:

public synchronized void method(){
   // 具體代碼
}

  當(dāng)一個對象O1在不同的線程中執(zhí)行這個同步方法時,他們之間會形成互斥,達到同步的效果。但是這個對象所屬類的另一對象O2卻能夠調(diào)用這個被加了synchronized關(guān)鍵字的方法。 每個對象實例對應(yīng)一把鎖,線程只有獲得對象實例的鎖才能執(zhí)行它的synchronized方法。 如果一個對象有多個synchronized方法,只要一個線程訪問了其中的一個synchronized方法,其它線程不能同時訪問這個對象中任何一個synchronized方法。但是該類的其他對象實例的 synchronized方法是不相干擾的。這種機制確保了同一時刻對于每一個對象實例,其所有聲明為 synchronized 的成員方法中至多只有一個處于可執(zhí)行狀態(tài)(因為至多只有一個能夠獲得該類實例對應(yīng)的鎖),從而有效避免了類成員變量的訪問沖突(只要所有可能訪問類成員變量的方法均被聲明為synchronized)。上邊的示例代碼等同于如下代碼:

public void method(){
   synchronized(this){
       //具體代碼
   }
}

  其中this指的是調(diào)用這個方法的對象,如O1??梢娡椒椒▽嵸|(zhì)是將synchronized作用于對象引用。只有獲得O1對象鎖的線程,才能夠調(diào)用O1的同步方法,而對O2而言,O1對象鎖和它互不關(guān)聯(lián),其他線程調(diào)用O2中的相同方法時,并不會產(chǎn)生同步阻塞。程序也可能在這種情形下擺脫同步機制的控制,造成數(shù)據(jù)混亂。sychronized修飾方法時需要注意以下3點:

(1)synchronized關(guān)鍵字不能繼承。

  雖然可以使用synchronized來定義方法,但synchronized并不屬于方法定義的一部分,因此,synchronized關(guān)鍵字不能被繼承。如果在父類中的某個方法使用了synchronized關(guān)鍵字,而在子類中覆蓋了這個方法,在子類中的這個方法默認(rèn)情況下并不是同步的,必須顯式地在子類為這個方法加上synchronized關(guān)鍵字才可以。當(dāng)然,還可以在子類方法中調(diào)用父類中相應(yīng)的方法,這樣雖然子類中的方法不是同步的,但子類調(diào)用了父類的同步方法,因此,子類的方法也就相當(dāng)于同步了。這兩種方式的示例代碼如下:
  手動加上synchronized修飾

class Parent{
  public synchronized void method() {}
}
class Child{
  public synchronized void method() {}
}

在子類中調(diào)用父類同步方法 

class Parent{
  public synchronized void method() {}
}
class Child{
  public synchronized void method() {}
}

(2)在定義接口方法時不能使用synchronized關(guān)鍵字。

(3)構(gòu)造方法不能使用synchronized關(guān)鍵字,但可以使用synchronized代碼塊來進行同步。

2.修飾一個靜態(tài)方法

  被修飾的方法被稱為靜態(tài)同步方法,其作用的范圍是整個靜態(tài)方法,鎖是靜態(tài)方法所屬的類(即Class對象)。所有需要獲得該類的任意對象的鎖,都會觸發(fā)同步。靜態(tài)同步方法的示例如下圖:

上述代碼中,雖然創(chuàng)建了SynThread類的兩個對象,但是該類中的run方法調(diào)用的是靜態(tài)同步方法,所以在運行過程中會同步執(zhí)行。因此,synchronized作用在靜態(tài)方法上時,可以防止多個線程同時訪問這個類中的靜態(tài)方法,它對類的所有實例對象都起作用。

3.修飾一個代碼塊

  被修飾的代碼塊稱為同步語句塊。synchronized的括號中必須傳入一個對象(實例對象或類的Class對象)作為鎖。其作用范圍是大括號{}括起來的代碼,鎖是Synchronized括號里指定的內(nèi)容。按照對象的類型可以分為類鎖和對象鎖。

(1)鎖對象為實例對象

public void method(Object o) {   
    synchronized(o) {          
       ...  
    }
}

上述代碼鎖定的就是o這個對象,只要進入以該對象為鎖的任何代碼都會觸發(fā)同步。當(dāng)有一個明確的對象作為鎖時,可以直接以該對象作為鎖。當(dāng)沒有明確的對象作為鎖,只是想讓一段代碼同步時,可以創(chuàng)建一個特殊的對象來充當(dāng)鎖。例如:

private byte[] lock = new byte[0];

注:查看編譯后的字節(jié)碼:生成零長度的byte[]對象只需3條操作碼,而Object lock = new Object()則需要7行操作碼。因此使用特殊對象來充當(dāng)鎖,大大節(jié)省了系統(tǒng)的開銷。

(2)鎖對象為類的Class對象

public class Demo{
  ...
  public static void method(){
    synchronized(Demo.class){
      ...
    }
  }
}

上述代碼是以Demo類的Class對象為鎖,進入以該類任意實例對象為鎖的代碼都會觸發(fā)同步,其效果類似于靜態(tài)同步方法。

四、synchronized的實現(xiàn)原理

monitor對象
  Java中的同步代碼塊是使用monitorenter和monitorexit指令實現(xiàn)的,其中monitorenter指令插入到同步代碼塊的開始位置,monitorexit指令插入到同步代碼塊的結(jié)束位置。JVM保證每一個monitorenter都有一個monitorexit與之相對應(yīng)。任何對象都有一個monitor與之相關(guān)聯(lián),當(dāng)線程執(zhí)行到monitorenter指令時,將會嘗試獲取鎖對象所對應(yīng)的monitor所有權(quán),即嘗試獲取對象的鎖;當(dāng)線程執(zhí)行monitorexit指令時,鎖的monitor就會被釋放。同步方法的實現(xiàn)與同步塊略有不同,它依靠的是方法修飾符上的ACC_SYNCHRONIZED實現(xiàn)。synchronized具體的實現(xiàn)原理詳見本人另一篇文章:
深入理解Java中Synchronized的實現(xiàn)原理

五、Synchronized與重入鎖ReentrantLock的區(qū)別

(1) 相對于ReentrantLock而言,synchronized鎖是重量級鎖,重量級體現(xiàn)在活躍性差一點。同時synchronized鎖是內(nèi)置鎖,意味著JVM能基于synchronized鎖做一些優(yōu)化:比如增加鎖的粒度(鎖粗化)、鎖消除。

(2) 在synchronized鎖上阻塞的線程是不可中斷的:線程A獲得了synchronized鎖,當(dāng)線程B也去獲取synchronized鎖時會被阻塞。而且線程B無法被其他線程中斷(不可中斷的阻塞),而ReentrantLock鎖能實現(xiàn)可中斷的阻塞。

(3) synchronized鎖釋放是自動的,當(dāng)線程執(zhí)行退出synchronized鎖保護的同步代碼塊時,會自動釋放synchronized鎖。而ReentrantLock需要顯示地釋放:即在try-finally塊中釋放鎖。

(4) 線程在競爭synchronized鎖時是非公平的:假設(shè)synchronized鎖目前被線程A占有,線程B請求鎖未果,被放入隊列中,線程C請求鎖未果,也被放入隊列中,線程D也來請求鎖,恰好此時線程A將鎖釋放了,那么線程D將跳過隊列中所有的等待線程并獲得這個鎖。而ReentrantLock能夠?qū)崿F(xiàn)鎖的公平性。

(5) synchronized鎖是讀寫互斥并且讀讀也互斥,ReentrantReadWriteLock 分為讀鎖和寫鎖,而讀鎖可以同時被多個線程持有,適合于讀多寫少場景的并發(fā)。

(6) ReentrantLock鎖的是代碼塊,synchronized還能鎖方法和類。ReentrantLock可以知道線程有沒有拿到鎖,而synchronized不能。

六、總結(jié)

(1) synchronized如果修飾的是代碼塊,則根據(jù)傳入內(nèi)容決定鎖的類型;synchronized如果修飾的是實例方法,它獲取的鎖是調(diào)用該方法的對象實例;synchronized如果修飾的是靜態(tài)方法,它獲取的鎖是調(diào)用該方法所屬的類,訪問該類所有的同步模塊都會加鎖(每個對象的同步方法、類的同步方法)。

(2) synchronized同步的關(guān)鍵要看鎖的類型
  如果是對象鎖(即同步塊傳入對象作為鎖或者實例同步方法),那么其他任意需要獲得該對象鎖的同步機制都會觸發(fā)加鎖。即線程進入一個同步實例方法,就會獲得其所屬對象的鎖。此時,如果其他線程進入該對象其他的同步實例方法或同步代碼塊時就會阻塞。
  如果是類鎖(即同步塊傳入類作為鎖或者靜態(tài)同步方法),那么其他任意需要獲得該類鎖的同步機制都會觸發(fā)加鎖。即線程進入一個靜態(tài)同步方法,就會獲得該方法所屬類的鎖。此時,如果其他線程再進入該類的其他同步方法(靜態(tài)或非靜態(tài)),或者進入獲取該類鎖的同步塊,都會阻塞。

(3) 對象的內(nèi)置鎖和對象的狀態(tài)之間沒有內(nèi)在的關(guān)聯(lián),雖然大多數(shù)類都將內(nèi)置鎖用作一種有效的加鎖機制,但對象的域并不一定通過內(nèi)置鎖來保護。當(dāng)獲取到與對象關(guān)聯(lián)的內(nèi)置鎖時,并不能阻止其他線程訪問該對象,當(dāng)某個線程獲得對象的鎖之后,只能阻止其他線程獲得同一個鎖。所以synchronized只是一個內(nèi)置鎖的加鎖機制,當(dāng)某個方法加上synchronized關(guān)鍵字后,就表明要獲得該內(nèi)置鎖才能執(zhí)行,并不能阻止其他線程訪問不需要獲得該內(nèi)置鎖的方法。

(4) 為什么要使用同步代碼塊?
  首先對程序來講同步的部分很影響運行效率,同步所覆蓋的代碼越多,對效率的影響就越嚴(yán)重。因此我們通常盡量縮小其影響范圍。所以就出現(xiàn)了同步代碼塊。只把一個方法中該同步的地方同步,并且同步代碼塊可以指定鎖對象。

(5) 使用synchronized進入一個臨界區(qū),會獲得對應(yīng)的鎖,退出時不管是否立即使用該對象的其他同步方法,都要立即釋放鎖,重新競爭獲得。如何連續(xù)使用一個對象的所有同步方法,不用中途釋放鎖,可以創(chuàng)建一個線程,使用一個同步代碼塊,同步鎖指定為該對象,然后在該代碼塊中可以連續(xù)調(diào)用該對象的同步方法。

(6) synchronized方法的缺陷
   a.實例同步方法鎖定的是調(diào)用這個同步方法的對象。也就是說,一個對象P1在不同的線程中執(zhí)行其同步方法時會產(chǎn)生互斥達到同步效果。但是P1對象所屬類所創(chuàng)建的另一對象P2卻可以調(diào)用這個同步方法。同步方法實質(zhì)是將synchronized作用于對象引用。只有拿到P1對象鎖的線程,才可以調(diào)用P1的同步方法,而對P2而言,P1這個鎖與它毫不相干,程序也可能在這種情形下擺脫同步機制的控制,造成數(shù)據(jù)混亂。
  b.若將一個代碼量大的方法聲明為synchronized將會大大影響效率。典型地,若將線程類的方法 run() 聲明為 synchronized ,由于在線程的整個生命期內(nèi)它一直在運行,因此將導(dǎo)致它對本類任何 synchronized 方法的調(diào)用都不會成功。

到此這篇關(guān)于Java同步鎖synchronized用法的最全總結(jié)的文章就介紹到這了,更多相關(guān)Java synchronized的總結(jié)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot整合spring-data-jpa的方法

    SpringBoot整合spring-data-jpa的方法

    這篇文章主要介紹了SpringBoot整合spring-data-jpa的方法,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-06-06
  • SpringBoot集成Redisson操作Redis的實現(xiàn)方法

    SpringBoot集成Redisson操作Redis的實現(xiàn)方法

    Redisson是一個用于Java的Redis客戶端,它提供了在分布式環(huán)境下操作Redis數(shù)據(jù)庫的簡單、高效的方式,本文主要介紹了SpringBoot集成Redisson操作Redis的實現(xiàn)方法,具有一定的參考價值,感興趣的可以了解一下
    2024-03-03
  • Spring向頁面?zhèn)髦岛徒邮茼撁鎮(zhèn)鬟^來的參數(shù)詳解

    Spring向頁面?zhèn)髦岛徒邮茼撁鎮(zhèn)鬟^來的參數(shù)詳解

    這篇文章主要給大家介紹了關(guān)于Spring向頁面?zhèn)髦岛徒邮茼撁鎮(zhèn)鬟^來的參數(shù)的相關(guān)資料,文中介紹的非常詳細(xì),對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。
    2017-06-06
  • 關(guān)于bootstrap.yml和bootstrap.properties的優(yōu)先級問題

    關(guān)于bootstrap.yml和bootstrap.properties的優(yōu)先級問題

    這篇文章主要介紹了關(guān)于bootstrap.yml和bootstrap.properties的優(yōu)先級問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • javaweb實現(xiàn)投票系統(tǒng)

    javaweb實現(xiàn)投票系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了javaweb實現(xiàn)投票系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • Mybatis-Plus的應(yīng)用場景描述及注入SQL原理分析

    Mybatis-Plus的應(yīng)用場景描述及注入SQL原理分析

    MyBatis-Plus是一個 MyBatis 的增強工具,在 MyBatis 的基礎(chǔ)上只做增強不做改變,為簡化開發(fā)、提高效率而生,本文重點給大家介紹Mybatis-Plus的應(yīng)用場景及注入SQL原理分析,感興趣的朋友跟隨小編一起學(xué)習(xí)吧
    2021-05-05
  • Java反轉(zhuǎn)字符串的五種方法總結(jié)

    Java反轉(zhuǎn)字符串的五種方法總結(jié)

    這篇文章主要介紹了五種在Java中反轉(zhuǎn)字符串的方法,包括使用StringBuilder的reverse()方法、字符數(shù)組、自定義StringBuilder方法、直接反轉(zhuǎn)以及Java8的StreamAPI,每種方法都有其特點和適用場景,需要的朋友可以參考下
    2025-03-03
  • java 學(xué)習(xí)筆記(入門篇)_java的安裝與配置

    java 學(xué)習(xí)筆記(入門篇)_java的安裝與配置

    學(xué)習(xí)Java已經(jīng)很長時間了,由于基礎(chǔ)不好遇到問題就無從下手,所以,打算寫Java的隨手筆記來鞏固基礎(chǔ),加強學(xué)習(xí),接下來講解java的安裝,配置等,感興趣的朋友可以參考下
    2013-01-01
  • @Query注解的原生用法和native用法解析

    @Query注解的原生用法和native用法解析

    這篇文章主要介紹了@Query注解的原生用法和native用法解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Java String初始化String域例題解析

    Java String初始化String域例題解析

    這篇文章主要介紹了Java String初始化String域例題解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-10-10

最新評論