0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

synchronized 的幾種錯(cuò)誤用法

科技綠洲 ? 來源:Java技術(shù)指北 ? 作者:Java技術(shù)指北 ? 2023-10-09 10:25 ? 次閱讀

synchronized 在我們平常工作中也是挺常用的, 對(duì)于擺脫多線程問題很有幫助。但是如果synchronized被錯(cuò)誤使用時(shí),可能會(huì)給我們帶來很多麻煩。

在本文中,我們將討論與同步相關(guān)的一些不好的做法,以及針對(duì)每個(gè)使用情況的更好的方法。

同步的原則

一般來說,我們應(yīng)該只對(duì)那些我們確信沒有外部代碼會(huì)鎖定的對(duì)象進(jìn)行同步。

換句話說,使用池化或可重復(fù)使用的對(duì)象進(jìn)行同步是一種不好的做法。原因是池化/可重用對(duì)象可以被JVM中的其他進(jìn)程訪問,外部/不被信任的代碼對(duì)這些對(duì)象的任何修改都會(huì)導(dǎo)致死鎖和非確定性行為。

現(xiàn)在,讓我們來討論基于某些類型的同步原則,如String、Boolean、Integer和Object。

String 字面量

1.錯(cuò)誤用法

字符串字面量是有池的,在Java中經(jīng)常被重復(fù)使用。因此,不建議使用String類型與 synchronized關(guān)鍵字進(jìn)行同步。

public void stringBadPractice1() {
    String stringLock = "LOCK_STRING";
    synchronized (stringLock) {
        // ...
    }
}

同樣地,如果我們使用private final String字面,它仍然是從常量池中引用的。

private final String stringLock = "LOCK_STRING";
public void stringBadPractice2() {
    synchronized (stringLock) {
        // ...
    }
}

此外,為了同步,內(nèi)接字符串被認(rèn)為是不好的做法。

private final String internedStringLock = new String("LOCK_STRING").intern();
public void stringBadPractice3() {
  synchronized (internedStringLock) {
      // ...
  }
}

根據(jù)Javadocs,intern方法為我們獲得了String對(duì)象的規(guī)范表示。換句話說,intern方法從池中返回一個(gè)String--如果它不在池中,則明確地將它添加到池中--它的內(nèi)容與這個(gè)String相同。

因此,在可重用對(duì)象上的同步問題對(duì)于內(nèi)部的String對(duì)象也是存在的。

注意:所有的String字面符號(hào)和以字符串為值的常量表達(dá)式都是自動(dòng)實(shí)現(xiàn)的。

2.正確用法

為了避免在String字面上進(jìn)行同步的不良做法,建議使用new關(guān)鍵字創(chuàng)建一個(gè)新的String實(shí)例。

讓我們?cè)谝呀?jīng)討論過的代碼中解決這個(gè)問題。首先,我們將創(chuàng)建一個(gè)新的String對(duì)象,以擁有一個(gè)唯一的引用(避免任何重復(fù)使用)和它自己的內(nèi)在鎖,這有助于同步。

然后,我們保持該對(duì)象的private和final,以防止任何外部/不受信任的代碼訪問它。

private final String stringLock = new String("LOCK_STRING");
public void stringSolution() {
    synchronized (stringLock) {
        // ...
    }
}

Boolean 字面量

Boolean類型有兩個(gè)值,即true和false,不適合用于鎖定目的。與JVM中的String字面量類似,boolean字面量也共享Boolean類的唯一實(shí)例。

讓我們來看看一個(gè)在Boolean鎖對(duì)象上同步的錯(cuò)誤用法例子。

private final Boolean booleanLock = Boolean.FALSE;
public void booleanBadPractice() {
    synchronized (booleanLock) {
        // ...
    }
}

在這里,如果任何外部代碼也在具有相同值的Boolean字面上進(jìn)行同步,系統(tǒng)就會(huì)變得沒有反應(yīng),或者導(dǎo)致死鎖的情況。

因此,我們不建議使用Boolean對(duì)象作為同步鎖。

原始類型的包裝類

1. 錯(cuò)誤用法

與boolean字段類似,原始類型的包裝類可能會(huì)重復(fù)使用某些值的實(shí)例。原因是JVM會(huì)緩存和共享可以表示為字節(jié)的值。

例如,讓我們寫一個(gè)在 Integer 上進(jìn)行同步的錯(cuò)誤用法例子。

private int count = 0;
private final Integer intLock = count; 
public void boxedPrimitiveBadPractice() { 
    synchronized (intLock) {
        count++;
        // ... 
    } 
}

2.正確用法

然而,與boolean字面量不同,在原始類型的包裝類上同步的解決方案是創(chuàng)建一個(gè)新實(shí)例。

與String對(duì)象類似,我們應(yīng)該使用new關(guān)鍵字來創(chuàng)建一個(gè)唯一的Integer對(duì)象的實(shí)例,該實(shí)例有自己的內(nèi)在鎖,并保持其private和final。

private int count = 0;
private final Integer intLock = new Integer(count);
public void boxedPrimitiveSolution() {
    synchronized (intLock) {
        count++;
        // ...
    }
}

類同步

當(dāng)一個(gè)類用this關(guān)鍵字實(shí)現(xiàn)方法同步或塊同步時(shí),JVM使用對(duì)象本身作為監(jiān)視器(其固有鎖)。

不受信任的代碼可以獲得并無限期地持有一個(gè)可訪問類的內(nèi)在鎖。因此,這可能會(huì)導(dǎo)致死鎖的情況。

1.錯(cuò)誤用法

例如,讓我們創(chuàng)建Animal類,它有一個(gè)synchronized方法setName和一個(gè)帶有synchronized塊的方法setOwner。

public class Animal {
    private String name;
    private String owner;
    
    // getters and constructors
    
    public synchronized void setName(String name) {
        this.name = name;
    }

    public void setOwner(String owner) {
        synchronized (this) {
            this.owner = owner;
        }
    }
}

現(xiàn)在,讓我們寫一些錯(cuò)誤用法,創(chuàng)建一個(gè)Animal類的實(shí)例,并對(duì)其進(jìn)行同步。

Animal animalObj = new Animal("Tommy", "John");
synchronized (animalObj) {
    while(true) {
        Thread.sleep(Integer.MAX_VALUE);
    }
}

在這里,不受信任的代碼例子引入了一個(gè)無限期的延遲,阻止了setName和setOwner方法的實(shí)現(xiàn)獲得同一個(gè)鎖。

2.正確用法

防止這個(gè)漏洞的解決方案是私人鎖對(duì)象。

我們的想法是使用與我們類中定義的Object類的private final實(shí)例相關(guān)的內(nèi)在鎖來代替對(duì)象本身的內(nèi)在鎖。

另外,我們應(yīng)該使用塊同步來代替方法同步,以增加靈活性,使非同步的代碼不在塊中。

所以,讓我們對(duì)我們的Animal類進(jìn)行必要的修改。

public class Animal {
    // ...

    private final Object objLock1 = new Object();
    private final Object objLock2 = new Object();

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

    public void setOwner(String owner) {
        synchronized (objLock2) {
            this.owner = owner;
        }
    }
}

在這里,為了提高并發(fā)性,我們通過定義多個(gè)private final鎖對(duì)象來細(xì)化鎖定方案,以分離我們對(duì)兩個(gè)方法--setName和setOwner的同步關(guān)注。

此外,如果實(shí)現(xiàn)同步塊的方法修改了一個(gè)靜態(tài)變量,我們必須通過鎖定靜態(tài)對(duì)象來實(shí)現(xiàn)同步。

private static int staticCount = 0;
private static final Object staticObjLock = new Object();
public void staticVariableSolution() {
    synchronized (staticObjLock) {
        count++;
        // ...
    }
}

總結(jié)

在這篇文章中,我們討論了一些與某些類型的同步有關(guān)的壞做法,如String、Boolean、Integer和Object。

本文最重要的啟示是,不建議使用池化或可重復(fù)使用的對(duì)象進(jìn)行同步。

另外,建議在Object類的private final實(shí)例上進(jìn)行同步。這樣的對(duì)象將無法被外部/不被信任的代碼訪問,否則這些代碼可能會(huì)與我們的公共類交互,從而減少這種交互導(dǎo)致死鎖的可能性。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 多線程
    +關(guān)注

    關(guān)注

    0

    文章

    277

    瀏覽量

    19899
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4722

    瀏覽量

    68236
  • string
    +關(guān)注

    關(guān)注

    0

    文章

    40

    瀏覽量

    4715
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Python學(xué)習(xí):“-m”選項(xiàng)典型用法和原理解析

    看了前面的幾種典型用法,你是否開始好奇:“-m”是怎么運(yùn)作的?它是怎么實(shí)現(xiàn)的?
    發(fā)表于 11-20 15:09 ?2956次閱讀
    Python學(xué)習(xí):“-m”選項(xiàng)典型<b class='flag-5'>用法</b>和原理解析

    C語言指針和數(shù)組的錯(cuò)誤用法

    上線后出現(xiàn)一個(gè)異常。我才覺得我對(duì)指針只是學(xué)廢了。找了一些指針和數(shù)組的博客資料,記錄一下。希望下次不會(huì)再犯類似的錯(cuò)誤
    發(fā)表于 09-28 09:12 ?665次閱讀

    掌握find命令的這幾種用法,就沒有找不到的文件!

    今天浩道跟大家分享linux下關(guān)于find命令的幾種經(jīng)典用法,讓你在linux茫茫的文件海洋中可以找出任何想要的文件!
    發(fā)表于 12-06 14:19 ?794次閱讀

    常見的幾種日期對(duì)象用法

    前的 util.Date 以及 Calander 使用起來更加的方便直觀,下面介紹幾種常見的日期對(duì)象用法。 LocalDateTime:日期加時(shí)間的日期對(duì)象,包含年月日時(shí)分秒 LocalDate:日期類,包含年月日
    的頭像 發(fā)表于 09-25 11:10 ?697次閱讀
    常見的<b class='flag-5'>幾種</b>日期對(duì)象<b class='flag-5'>用法</b>

    while的使用形式有哪幾種?分別有什么用法?

    請(qǐng)問下while的使用形式有哪幾種?分別有什么用法?
    發(fā)表于 07-15 12:29

    C語言中的數(shù)據(jù)類型有哪幾種?const有哪些用法?

    C語言中的數(shù)據(jù)類型有哪幾種?const有哪些用法?作用域與static用法是什么?extern是如何去使用的?
    發(fā)表于 07-22 06:51

    Synchronized multi-spark modul

    Synchronized multi-spark module (SMSM) for Electronic Ignition Devices (EID)
    發(fā)表于 12-29 09:09 ?824次閱讀
    <b class='flag-5'>Synchronized</b> multi-spark modul

    關(guān)于緩存的四大誤用,你中招了嗎?

    緩存,是互聯(lián)網(wǎng)分層架構(gòu)中,非常重要的一個(gè)部分,通常用它來降低數(shù)據(jù)庫(kù)壓力,提升系統(tǒng)整體性能,縮短訪問時(shí)間。 有架構(gòu)師說“緩存是萬金油,哪里有問題,加個(gè)緩存,就能優(yōu)化”,緩存的濫用,可能會(huì)導(dǎo)致一些錯(cuò)誤用法。
    發(fā)表于 07-01 10:00 ?3143次閱讀
    關(guān)于緩存的四大<b class='flag-5'>誤用</b>,你中招了嗎?

    Java并發(fā)編程中線程同步的常用手段synchronized用法

    變量的修改能夠及時(shí)可見,獲得鎖的線程操作完畢后會(huì)將所數(shù)據(jù)刷新到共享內(nèi)存區(qū)[1] 有序性:不解決重排序,但保證有序性 synchronized用法有三個(gè): 修飾實(shí)例方法 修飾靜態(tài)方法 修飾代碼塊 1. 修飾實(shí)例方法 synchronize
    的頭像 發(fā)表于 04-04 11:30 ?1152次閱讀
    Java并發(fā)編程中線程同步的常用手段<b class='flag-5'>synchronized</b><b class='flag-5'>用法</b>

    講“伏秒平衡”,驗(yàn)證磁性元件的錯(cuò)誤用法資料下載

    電子發(fā)燒友網(wǎng)為你提供講“伏秒平衡”,驗(yàn)證磁性元件的錯(cuò)誤用法資料下載的電子資料下載,更有其他相關(guān)的電路圖、源代碼、課件教程、中文資料、英文資料、參考設(shè)計(jì)、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
    發(fā)表于 04-03 08:47 ?15次下載
    講“伏秒平衡”,驗(yàn)證磁性元件的<b class='flag-5'>錯(cuò)誤用法</b>資料下載

    詳細(xì)介紹synchronized和Object的關(guān)鍵方法和虛擬機(jī)實(shí)現(xiàn)原理

    編程過程中經(jīng)常會(huì)遇到線程的同步問題,Java 中對(duì)同步問題的解決方案比較多(synchronized、JUC、原子操作、volatile、條件變量等),其中synchronized 最方便、簡(jiǎn)單易用,也是java 編程中使用最多的臨界區(qū)保護(hù)方案。
    的頭像 發(fā)表于 03-13 10:06 ?1198次閱讀

    synchronized知識(shí)合集1

    * 線程安全 * 什么是synchronized關(guān)鍵字? * synchronized實(shí)現(xiàn)方式 * 1.修飾實(shí)例方法 * 2.修飾靜態(tài)方法 * 3.修飾代碼塊
    的頭像 發(fā)表于 05-11 11:07 ?425次閱讀
    <b class='flag-5'>synchronized</b>知識(shí)合集1

    synchronized知識(shí)合集2

    * 線程安全 * 什么是synchronized關(guān)鍵字? * synchronized實(shí)現(xiàn)方式 * 1.修飾實(shí)例方法 * 2.修飾靜態(tài)方法 * 3.修飾代碼塊
    的頭像 發(fā)表于 05-11 11:08 ?380次閱讀

    synchronized的原理與四種用法介紹

    JDK提供的鎖分兩種,一種是JVM實(shí)現(xiàn)的synchronized,是java的關(guān)鍵字,因此在這個(gè)關(guān)鍵字作用對(duì)象的范圍內(nèi)都是可以保證原子性的,主要是依賴特殊的CPU指令。另一種是JDK提供的代碼層面的鎖Lock。
    的頭像 發(fā)表于 06-09 16:13 ?965次閱讀
    <b class='flag-5'>synchronized</b>的原理與四種<b class='flag-5'>用法</b>介紹

    synchronized的鎖膨脹

    初識(shí) synchronized 可以加在方法和類上面,作用于類和對(duì)象。下面代碼中列出了 synchronized用法。 public class SynchronizedTest
    的頭像 發(fā)表于 10-10 16:58 ?444次閱讀
    <b class='flag-5'>synchronized</b>的鎖膨脹