那曲檬骨新材料有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

synchronized 的幾種錯誤用法

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

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

在本文中,我們將討論與同步相關的一些不好的做法,以及針對每個使用情況的更好的方法。

同步的原則

一般來說,我們應該只對那些我們確信沒有外部代碼會鎖定的對象進行同步。

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

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

String 字面量

1.錯誤用法

字符串字面量是有池的,在Java中經常被重復使用。因此,不建議使用String類型與 synchronized關鍵字進行同步。

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

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

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

此外,為了同步,內接字符串被認為是不好的做法。

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

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

因此,在可重用對象上的同步問題對于內部的String對象也是存在的。

注意:所有的String字面符號和以字符串為值的常量表達式都是自動實現的。

2.正確用法

為了避免在String字面上進行同步的不良做法,建議使用new關鍵字創建一個新的String實例。

讓我們在已經討論過的代碼中解決這個問題。首先,我們將創建一個新的String對象,以擁有一個唯一的引用(避免任何重復使用)和它自己的內在鎖,這有助于同步。

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

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

Boolean 字面量

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

讓我們來看看一個在Boolean鎖對象上同步的錯誤用法例子。

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

在這里,如果任何外部代碼也在具有相同值的Boolean字面上進行同步,系統就會變得沒有反應,或者導致死鎖的情況。

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

原始類型的包裝類

1. 錯誤用法

與boolean字段類似,原始類型的包裝類可能會重復使用某些值的實例。原因是JVM會緩存和共享可以表示為字節的值。

例如,讓我們寫一個在 Integer 上進行同步的錯誤用法例子。

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

2.正確用法

然而,與boolean字面量不同,在原始類型的包裝類上同步的解決方案是創建一個新實例。

與String對象類似,我們應該使用new關鍵字來創建一個唯一的Integer對象的實例,該實例有自己的內在鎖,并保持其private和final。

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

類同步

當一個類用this關鍵字實現方法同步或塊同步時,JVM使用對象本身作為監視器(其固有鎖)。

不受信任的代碼可以獲得并無限期地持有一個可訪問類的內在鎖。因此,這可能會導致死鎖的情況。

1.錯誤用法

例如,讓我們創建Animal類,它有一個synchronized方法setName和一個帶有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;
        }
    }
}

現在,讓我們寫一些錯誤用法,創建一個Animal類的實例,并對其進行同步。

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

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

2.正確用法

防止這個漏洞的解決方案是私人鎖對象。

我們的想法是使用與我們類中定義的Object類的private final實例相關的內在鎖來代替對象本身的內在鎖。

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

所以,讓我們對我們的Animal類進行必要的修改。

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;
        }
    }
}

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

此外,如果實現同步塊的方法修改了一個靜態變量,我們必須通過鎖定靜態對象來實現同步。

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

總結

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

本文最重要的啟示是,不建議使用池化或可重復使用的對象進行同步。

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

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 多線程
    +關注

    關注

    0

    文章

    278

    瀏覽量

    20075
  • 代碼
    +關注

    關注

    30

    文章

    4828

    瀏覽量

    69055
  • string
    +關注

    關注

    0

    文章

    40

    瀏覽量

    4748
收藏 人收藏

    評論

    相關推薦

    Python學習:“-m”選項典型用法和原理解析

    看了前面的幾種典型用法,你是否開始好奇:“-m”是怎么運作的?它是怎么實現的?
    發表于 11-20 15:09 ?3042次閱讀
    Python學習:“-m”選項典型<b class='flag-5'>用法</b>和原理解析

    C語言指針和數組的錯誤用法

    上線后出現一個異常。我才覺得我對指針只是學廢了。找了一些指針和數組的博客資料,記錄一下。希望下次不會再犯類似的錯誤
    發表于 09-28 09:12 ?771次閱讀

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

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

    常見的幾種日期對象用法

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

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

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

    C語言中的數據類型有哪幾種?const有哪些用法

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

    Synchronized multi-spark modul

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

    關于緩存的四大誤用,你中招了嗎?

    緩存,是互聯網分層架構中,非常重要的一個部分,通常用它來降低數據庫壓力,提升系統整體性能,縮短訪問時間。 有架構師說“緩存是萬金油,哪里有問題,加個緩存,就能優化”,緩存的濫用,可能會導致一些錯誤用法
    發表于 07-01 10:00 ?3197次閱讀
    關于緩存的四大<b class='flag-5'>誤用</b>,你中招了嗎?

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

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

    講“伏秒平衡”,驗證磁性元件的錯誤用法資料下載

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

    詳細介紹synchronized和Object的關鍵方法和虛擬機實現原理

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

    synchronized知識合集1

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

    synchronized知識合集2

    * 線程安全 * 什么是synchronized關鍵字? * synchronized實現方式 * 1.修飾實例方法 * 2.修飾靜態方法 * 3.修飾代碼塊
    的頭像 發表于 05-11 11:08 ?432次閱讀

    synchronized的原理與四種用法介紹

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

    synchronized的鎖膨脹

    初識 synchronized 可以加在方法和類上面,作用于類和對象。下面代碼中列出了 synchronized用法。 public class SynchronizedTest
    的頭像 發表于 10-10 16:58 ?533次閱讀
    <b class='flag-5'>synchronized</b>的鎖膨脹
    威尼斯人娱乐城备用地址| 威尼斯人娱乐城送钱| 大发888真钱娱乐游戏| 百家乐官网游戏算牌| 百家乐官网suncity| 电子百家乐打法| 网络轮盘| 百家乐官网在线赌场| 凯旋门百家乐技巧| 大发888开户注册哪家好| 川宜百家乐官网注册号| 百家乐必胜软件下载| 波克棋牌免费下载| 网上百家乐官网做假| 百家乐国际娱乐网| 广河县| 有钱人百家乐官网的玩法技巧和规则 | 亚洲百家乐官网博彩的玩法技巧和规则 | 宜昌市| 百家乐官网官方网站| 大发888娱乐场下载制度| 百家乐官网赌博故事| 网上百家乐真的假| 百家乐官网全程打庄| 太阳城百家乐娱乐开户| 新利国际网站| 百家乐的必赢术| 新濠国际娱乐| 百家乐概率投注| 德州扑克发牌规则| 千亿百家乐官网的玩法技巧和规则| 威尼斯人娱乐场 新世纪| 真人百家乐官网什么平台| 百家乐评测| 百家乐官网斗视频游戏| 百家乐道具扫描| 百家乐官网隔一数打法| 百家乐真人玩下载| 噢门百家乐官网玩的技巧| 网上赌百家乐有假| 希尔顿百家乐官网试玩|