日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

线程安全问题的 3 种解决方案!

發布時間:2025/3/11 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 线程安全问题的 3 种解决方案! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者 | 磊哥

來源 | Java面試真題解析(ID:aimianshi666)

轉載請聯系授權(微信ID:GG_Stone)

線程安全是指某個方法或某段代碼,在多線程中能夠正確的執行,不會出現數據不一致或數據污染的情況,我們把這樣的程序稱之為線程安全的,反之則為非線程安全的。在 Java 中,解決線程安全問題有以下 3 種手段:

  • 使用線程安全類,比如 AtomicInteger。

  • 加鎖排隊執行

  • 使用 synchronized 加鎖。

  • 使用 ReentrantLock 加鎖。

  • 使用線程本地變量 ThreadLocal。

  • 接下來我們逐個來看它們的實現。

    線程安全問題演示

    我們創建一個變量 number 等于 0,之后創建線程 1,執行 100 萬次 ++ 操作,同時再創建線程 2 執行 100 萬次 -- 操作,等線程 1 和線程 2 都執行完之后,打印 number 變量的值,如果打印的結果為 0,則說明是線程安全的,否則則為非線程安全的,示例代碼如下:

    public?class?ThreadSafeTest?{//?全局變量private?static?int?number?=?0;//?循環次數(100W)private?static?final?int?COUNT?=?1_000_000;public?static?void?main(String[]?args)?throws?InterruptedException?{//?線程1:執行 100W 次?++?操作Thread?t1?=?new?Thread(()?->?{for?(int?i?=?0;?i?<?COUNT;?i++)?{number++;}});t1.start();//?線程2:執行 100W 次?--?操作Thread?t2?=?new?Thread(()?->?{for?(int?i?=?0;?i?<?COUNT;?i++)?{number--;}});t2.start();//?等待線程?1?和線程?2,執行完,打印?number?最終的結果t1.join();t2.join();System.out.println("number 最終結果:"?+?number);} }

    以上程序的執行結果如下圖所示:從上述執行結果可以看出,number 變量最終的結果并不是 0,和預期的正確結果不相符,這就是多線程中的線程安全問題。

    解決線程安全問題

    1.原子類AtomicInteger

    AtomicInteger 是線程安全的類,使用它可以將 ++ 操作和 -- 操作,變成一個原子性操作,這樣就能解決非線程安全的問題了,如下代碼所示:

    import?java.util.concurrent.atomic.AtomicInteger;public?class?AtomicIntegerExample?{//?創建?AtomicIntegerprivate?static?AtomicInteger?number?=?new?AtomicInteger(0);//?循環次數private?static?final?int?COUNT?=?1_000_000;public?static?void?main(String[]?args)?throws?InterruptedException?{//?線程1:執行 100W 次?++?操作Thread?t1?=?new?Thread(()?->?{for?(int?i?=?0;?i?<?COUNT;?i++)?{//?++?操作number.incrementAndGet();}});t1.start();//?線程2:執行 100W 次?--?操作Thread?t2?=?new?Thread(()?->?{for?(int?i?=?0;?i?<?COUNT;?i++)?{//?--?操作number.decrementAndGet();}});t2.start();//?等待線程?1?和線程?2,執行完,打印?number?最終的結果t1.join();t2.join();System.out.println("最終結果:"?+?number.get());} }

    以上程序的執行結果如下圖所示:

    2.加鎖排隊執行

    Java 中有兩種鎖:synchronized 同步鎖和 ReentrantLock 可重入鎖。

    2.1 同步鎖synchronized

    synchronized 是 JVM 層面實現的自動加鎖和自動釋放鎖的同步鎖,它的實現代碼如下:

    public?class?SynchronizedExample?{//?全局變量private?static?int?number?=?0;//?循環次數(100W)private?static?final?int?COUNT?=?1_000_000;public?static?void?main(String[]?args)?throws?InterruptedException?{//?線程1:執行 100W 次?++?操作Thread?t1?=?new?Thread(()?->?{for?(int?i?=?0;?i?<?COUNT;?i++)?{//?加鎖排隊執行synchronized?(SynchronizedExample.class)?{number++;}}});t1.start();//?線程2:執行 100W 次?--?操作Thread?t2?=?new?Thread(()?->?{for?(int?i?=?0;?i?<?COUNT;?i++)?{//?加鎖排隊執行synchronized?(SynchronizedExample.class)?{number--;}}});t2.start();//?等待線程?1?和線程?2,執行完,打印?number?最終的結果t1.join();t2.join();System.out.println("number 最終結果:"?+?number);} }

    以上程序的執行結果如下圖所示:

    2.2 可重入鎖ReentrantLock

    ReentrantLock 可重入鎖需要程序員自己加鎖和釋放鎖,它的實現代碼如下:

    import?java.util.concurrent.locks.ReentrantLock;/***?使用?ReentrantLock?解決非線程安全問題*/ public?class?ReentrantLockExample?{//?全局變量private?static?int?number?=?0;//?循環次數(100W)private?static?final?int?COUNT?=?1_000_000;//?創建?ReentrantLockprivate?static?ReentrantLock?lock?=?new?ReentrantLock();public?static?void?main(String[]?args)?throws?InterruptedException?{//?線程1:執行 100W 次?++?操作Thread?t1?=?new?Thread(()?->?{for?(int?i?=?0;?i?<?COUNT;?i++)?{lock.lock();????//?手動加鎖number++;???????//?++?操作lock.unlock();??//?手動釋放鎖}});t1.start();//?線程2:執行 100W 次?--?操作Thread?t2?=?new?Thread(()?->?{for?(int?i?=?0;?i?<?COUNT;?i++)?{lock.lock();????//?手動加鎖number--;???????//?--?操作lock.unlock();??//?手動釋放鎖}});t2.start();//?等待線程?1?和線程?2,執行完,打印?number?最終的結果t1.join();t2.join();System.out.println("number 最終結果:"?+?number);} }

    以上程序的執行結果如下圖所示:

    3.線程本地變量ThreadLocal

    使用 ThreadLocal 線程本地變量也可以解決線程安全問題,它是給每個線程獨自創建了一份屬于自己的私有變量,不同的線程操作的是不同的變量,所以也不會存在非線程安全的問題,它的實現代碼如下:

    public?class?ThreadSafeExample?{//?創建?ThreadLocal(設置每個線程中的初始值為?0)private?static?ThreadLocal<Integer>?threadLocal?=?ThreadLocal.withInitial(()?->?0);//?全局變量private?static?int?number?=?0;//?循環次數(100W)private?static?final?int?COUNT?=?1_000_000;public?static?void?main(String[]?args)?throws?InterruptedException?{//?線程1:執行 100W 次?++?操作Thread?t1?=?new?Thread(()?->?{try?{for?(int?i?=?0;?i?<?COUNT;?i++)?{//?++?操作threadLocal.set(threadLocal.get()?+?1);}//?將?ThreadLocal?中的值進行累加number?+=?threadLocal.get();}?finally?{threadLocal.remove();?//?清除資源,防止內存溢出}});t1.start();//?線程2:執行 100W 次?--?操作Thread?t2?=?new?Thread(()?->?{try?{for?(int?i?=?0;?i?<?COUNT;?i++)?{//?--?操作threadLocal.set(threadLocal.get()?-?1);}//?將?ThreadLocal?中的值進行累加number?+=?threadLocal.get();}?finally?{threadLocal.remove();?//?清除資源,防止內存溢出}});t2.start();//?等待線程?1?和線程?2,執行完,打印?number?最終的結果t1.join();t2.join();System.out.println("最終結果:"?+?number);} }

    以上程序的執行結果如下圖所示:

    總結

    在 Java 中,解決線程安全問題的手段有 3 種:

    1.使用線程安全的類,如 AtomicInteger 類;

    2.使用鎖 synchronized 或 ReentrantLock 加鎖排隊執行;

    3.使用線程本地變量 ThreadLocal 來處理。

    是非審之于己,毀譽聽之于人,得失安之于數。

    公眾號:Java面試真題解析

    面試合集:https://gitee.com/mydb/interview

    往期推薦

    面試突擊36:線程安全問題是如何產生的?


    每周匯總 | Java面試題(共35篇)2022版


    面試突擊35:如何判斷線程池已經執行完所有任務了?


    總結

    以上是生活随笔為你收集整理的线程安全问题的 3 种解决方案!的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。