最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java多線程編程中的并發(fā)安全問題及解決方法

 更新時間:2023年04月19日 10:50:12   作者:胡尚  
保障多線程并發(fā)安全,解決線程同步與鎖競爭問題,提高應(yīng)用性能與可靠性。多線程編程需要考慮線程安全性,使用同步機制保證共享變量的一致性,避免線程競爭導(dǎo)致的數(shù)據(jù)不一致與死鎖等問題。常用的同步機制包括synchronized、ReentrantLock、volatile等

線程安全性

線程安全是指我們所寫的代碼在并發(fā)情況下使用時,總是能表現(xiàn)出正確的行為;反之,未實現(xiàn)線程安全的代碼,表現(xiàn)的行為是不可預(yù)知的,有可能正確,

實現(xiàn)線程安全的方式:

線程封閉

就是把對象封裝到一個線程里,只有這一個線程能看到此對象。實現(xiàn)線程封閉的方式如下:

棧封閉

這里是指每個線程自己的線程棧,方法的局部變量就是在線程棧中的,對于其他線程是不可見的

ThreadLocal

各個線程Thread對象維護了一份Map,對于其他線程是不可見的

無狀態(tài)的類

沒有任何成員變量的類,就叫無狀態(tài)的類,這種類一定是線程安全的。

讓類不可變

沒有成員變量的類畢竟是少數(shù),我們還可以讓類的成員變量不可變,給他們加上final關(guān)鍵字

如果成員變量是一個對象,final不能保證類的安全性,因為雖然對象的引用不會變,但是在堆上的對象實例可能被多個線程同時修改,沒有正確處理的情況下,對象實例在堆中的數(shù)據(jù)是不可預(yù)知的。

加鎖或CAS

synchronized、顯示鎖Look、原子Atomic操作、CAS機制等等

死鎖

定義

是指兩個或兩個以上的進程在執(zhí)行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無外力作用,它們都將無法推進下去。此時稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖。

  • 死鎖是必然發(fā)生在多操作者(M>=2 個)爭奪多個資源(N>=2 個,且 N<=M) 才會發(fā)生這種情況。
  • 爭奪資源的順序不對,如果爭奪資源的順序是一樣的,也不會產(chǎn)生死鎖;
  • 爭奪者對拿到的資源不放手,也不能被掠奪。

學(xué)術(shù)化的定義。死鎖的發(fā)生必須具備以下四個必要條件。

  • 互斥條件:指進程對所分配到的資源進行排它性使用,即在一段時間內(nèi)某資源只由一個進程占用。如果此時還有其它進程請求資源,則請求者只能等待, 直至占有資源的進程用畢釋放。
  • 請求和保持條件:指進程已經(jīng)保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程占有,此時請求進程阻塞,但又對自己已獲得的其它資源保持不放。
  • 不剝奪條件:指進程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。
  • 環(huán)路等待條件:指在發(fā)生死鎖時,必然存在一個進程——資源的環(huán)形鏈, 即進程集合{P0,P1,P2,···,Pn}中的 P0 正在等待一個 P1 占用的資源;P1 正在等待 P2 占用的資源,……,Pn 正在等待已被 P0 占用的資源。

只要打破四個必要條件之一就能有效預(yù)防死鎖的發(fā)生。

  • 打破互斥條件:改造獨占性資源為虛擬資源,大部分資源已無法改造。
  • 打破不可搶占條件:當(dāng)一進程占有一獨占性資源后又申請一獨占性資源而無 法滿足,則退出原占有的資源。
  • 打破占有且申請條件:采用資源預(yù)先分配策略,即進程運行前申請全部資源, 滿足則運行,不然就等待,這樣就不會占有且申請。
  • 打破循環(huán)等待條件:實現(xiàn)資源有序分配策略,對所有設(shè)備實現(xiàn)分類編號,所有進程只能采用按序號遞增的形式申請資源。

避免死鎖常見的算法有有序資源分配法、銀行家算法。

實現(xiàn)一個死鎖

/**
 * @Description: 實現(xiàn)一個簡單的死鎖程序
 */
public class DeadLookTest {
    private static Object o1 = new Object();
    private static Object o2 = new Object();
    public void fastLock() throws InterruptedException {
        synchronized(o1){
            Thread.sleep(2000);
            System.out.println("fast");
            synchronized (o2){
                System.out.println("----");
            }
        }
    }
    public void postLock() throws InterruptedException {
        synchronized(o2){
            Thread.sleep(2000);
            System.out.println("post");
            synchronized (o1){
                System.out.println("----");
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        DeadLookTest deadlook = new DeadLookTest();
        // 新開一個線程去調(diào)用其中一個方法
        new Thread(() -> {
            try {
                deadlook.postLock();
            } catch (InterruptedException e) {
            }
        }).start();
        deadlook.fastLock();
    }
}

查看死鎖

使用jstack命令查看死鎖結(jié)果

C:\Users\Administrator>jps
4784 DeadLookTest
9808 RemoteMavenServer36
2052 Launcher
2692 Jps
8572

C:\Users\Administrator>jstack 4784
......
Found one Java-level deadlock:
=============================
"Thread-0":
  waiting to lock monitor 0x0000025d5a58fd88 (object 0x0000000716edb910, a java.lang.Object),
  which is held by "main"
"main":
  waiting to lock monitor 0x0000025d5a58e998 (object 0x0000000716edb920, a java.lang.Object),
  which is held by "Thread-0"

Java stack information for the threads listed above:
===================================================
"Thread-0":
        at cn.tulingxueyuan.safe.dl.DeadLookTest.postLock(DeadLookTest.java:30)
        - waiting to lock <0x0000000716edb910> (a java.lang.Object)
        - locked <0x0000000716edb920> (a java.lang.Object)
        at cn.tulingxueyuan.safe.dl.DeadLookTest.lambda$main$0(DeadLookTest.java:39)
        at cn.tulingxueyuan.safe.dl.DeadLookTest$$Lambda$1/2003749087.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)
"main":
        at cn.tulingxueyuan.safe.dl.DeadLookTest.fastLock(DeadLookTest.java:20)
        - waiting to lock <0x0000000716edb920> (a java.lang.Object)
        - locked <0x0000000716edb910> (a java.lang.Object)
        at cn.tulingxueyuan.safe.dl.DeadLookTest.main(DeadLookTest.java:43)

Found 1 deadlock.

解決死鎖

我們現(xiàn)在通過上面的命令找到了產(chǎn)生死鎖的位置,那么如何取解決死鎖嘞?我們知道產(chǎn)生死鎖的原因如下:

  • 死鎖是必然發(fā)生在多操作者(M>=2 個)爭奪多個資源(N>=2 個,且 N<=M) 才會發(fā)生這種情況。
  • 爭奪資源的順序不對,如果爭奪資源的順序是一樣的,也不會產(chǎn)生死鎖;
  • 爭奪者對拿到的資源不放手,也不能被掠奪。

第一個條件一般都是業(yè)務(wù)必須要,那么打破死鎖就要從下面的兩個條件去解決

  • 保證爭奪鎖資源的順序一樣。

在實際的開發(fā)中可能會存在比較隱蔽的加鎖順序,比如鎖對象作為方法參數(shù)傳遞,如下所示

private static void businessDo(Object first,Object second) throws InterruptedException {
    String threadName = Thread.currentThread().getName();
    synchronized (first){
        System.out.println(threadName + " get first");
        Thread.sleep(1000);
        synchronized (second){
            System.out.println(threadName + " get second");
        }
    }
}
// 然后兩個線程,在調(diào)用的時候傳遞的參數(shù)順序卻不一樣
businessDo(No1,No2);
businessDo(No2,No1);

解決上面這種問題的方式是:在加鎖前,在方法中做一個內(nèi)部的排序

public class SafeOperate {
    private static Object No13 = new Object();//第一個鎖
    private static Object No14 = new Object();//第二個鎖
    private static Object tieLock = new Object();//第三把鎖
    public void transfer(Object first,Object second) throws InterruptedException {
        int firstHash = System.identityHashCode(first);
        int secondHash = System.identityHashCode(second);
        if(firstHash<secondHash){
            synchronized (first){
                System.out.println(Thread.currentThread().getName()+" get "+first);
                Thread.sleep(100);
                synchronized (second){
                    System.out.println(Thread.currentThread().getName()+" get "+second);
                }
            }
        }else if(secondHash<firstHash){
            synchronized (second){
                System.out.println(Thread.currentThread().getName()+" get"+second);
                Thread.sleep(100);
                synchronized (first){
                    System.out.println(Thread.currentThread().getName()+" get"+first);
                }
            }
        }else{
            // 萬一兩個對象的hash值一樣,那么就引入第三把鎖,誰先搶到第三把鎖就去進行前兩兩把鎖的加鎖
            synchronized (tieLock){
                synchronized (first){
                    synchronized (second){
                        System.out.println(Thread.currentThread().getName()+" get"+first);
                        System.out.println(Thread.currentThread().getName()+" get"+second);
                    }
                }
            }
        }
    }
}

對拿到的鎖資源嘗試釋放

這種方式對于synchronized是不適用的,因為它不拿到鎖誓不罷休。使用ReentrantLock,使用其中的tryLock(long time, TimeUnit unit)方法,在指定的時間中如果還沒有拿到鎖就去進行釋放的邏輯

大致是實現(xiàn)邏輯如下所示

while(true){
    if(No13.tryLock()){
        System.out.println(threadName +" get 13");
        // 如果沒有拿到No14的鎖,那么No13的鎖也釋放
        try{
            if(No14.tryLock()){
                try{
                    System.out.println(threadName  +" get 14");
                    System.out.println("zhouYuDo do work------------");
                    break;
                }finally{
                    No14.unlock();
                }
            }
        }finally {
            No13.unlock();
        }
    }
    // 如果不加休眠機制,那么就比較容易產(chǎn)生活鎖
    Thread.sleep(1000);
}

其他線程安全問題

活鎖

兩個線程在嘗試拿鎖的機制中,發(fā)生多個線程之間互相謙讓,不斷發(fā)生同一個線程總是拿到同一把鎖,在嘗試拿另一把鎖時因為拿不到,而將本來已經(jīng)持有的鎖釋放的過程。

解決辦法:每個線程休眠隨機數(shù),錯開拿鎖的時間。

線程饑餓

低優(yōu)先級的線程,總是拿不到執(zhí)行時間

單例模式

  • DCL雙重檢測機制
  • volatile關(guān)鍵字禁止指令重排
public class HungrySingleton {
    //創(chuàng)建 SingletonHungry 的一個對象
    private static volatile HungrySingleton instance;
    // 讓構(gòu)方法私有,這樣該類就不會被其它類實例化
    private HungrySingleton() {
    }
    //獲取唯一可用的對象
    public static HungrySingleton getInstance() {
        if(null == instance) { 
            synchronized{
                if(null == instance) { 
                    instance = new LazySingleton();
                }
            }
        }
    	return instance;
    }
}

到此這篇關(guān)于Java多線程編程中的并發(fā)安全問題及解決方法的文章就介紹到這了,更多相關(guān)Java多線程并發(fā)安全內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 使用SpringBoot簡單實現(xiàn)一個蘋果支付的場景

    使用SpringBoot簡單實現(xiàn)一個蘋果支付的場景

    這篇文章主要為大家詳細(xì)介紹了如何在Spring?Boot項目中集成Apple?Pay功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-11-11
  • Java版坦克大戰(zhàn)游戲源碼示例

    Java版坦克大戰(zhàn)游戲源碼示例

    本篇文章主要介紹了Java版坦克大戰(zhàn)游戲源碼示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • Java如何使用itext向PDF插入數(shù)據(jù)和圖片

    Java如何使用itext向PDF插入數(shù)據(jù)和圖片

    最近項目中使用到Java實現(xiàn)寫入PDF文件,這篇文章主要給大家介紹了關(guān)于Java如何使用itext向PDF插入數(shù)據(jù)和圖片的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-01-01
  • 使用Spring啟動時運行自定義業(yè)務(wù)

    使用Spring啟動時運行自定義業(yè)務(wù)

    這篇文章主要介紹了使用Spring啟動時運行自定義業(yè)務(wù)的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 解決IDEA中同項目引用報紅問題

    解決IDEA中同項目引用報紅問題

    在IDEA中,如果項目引用報紅,可能是因為IDEA的引用緩存問題,可以通過File->Invalidate Caches/Restart清空緩存并重建索引來解決,這個方法可以幫助解決同項目中引用找不到的問題,恢復(fù)正常的項目引用,消除報紅
    2024-09-09
  • 淺談Thread.sleep()為什么要拋出中斷異常

    淺談Thread.sleep()為什么要拋出中斷異常

    本文主要介紹了淺談Thread.sleep()為什么要拋出中斷異常,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Java Socket 編程詳解

    Java Socket 編程詳解

    Java Socket 編程是指使用 Java 語言進行網(wǎng)絡(luò)通信的過程,包括建立連接、傳輸數(shù)據(jù)和關(guān)閉連接等操作,本文將詳細(xì)介紹Java Socket編程,需要的朋友可以參考下
    2023-05-05
  • 基于Spring BeanUtils的copyProperties方法使用及注意事項

    基于Spring BeanUtils的copyProperties方法使用及注意事項

    這篇文章主要介紹了基于Spring BeanUtils的copyProperties方法使用及注意事項,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • 一篇文章帶你了解java接口與繼承

    一篇文章帶你了解java接口與繼承

    這篇文章主要介紹了Java接口和繼承操作,結(jié)合具體實例形式分析了Java接口和繼承與使用的相關(guān)原理、操作技巧與注意事項,需要的朋友可以參考下
    2021-08-08
  • java對list<Object>進行手動分頁實現(xiàn)

    java對list<Object>進行手動分頁實現(xiàn)

    本文主要介紹了java對list<Object>進行手動分頁實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07

最新評論

西丰县| 长兴县| 满洲里市| 平阳县| 湖州市| 临城县| 衡南县| 洛浦县| 泸溪县| 浦北县| 梓潼县| 邮箱| 无棣县| 桐梓县| 昌平区| 铁力市| 梨树县| 剑河县| 炉霍县| 禄丰县| 芒康县| 长顺县| 漯河市| 任丘市| 青川县| 武邑县| 肃宁县| 定西市| 定陶县| 宜州市| 江西省| 郧西县| 锡林浩特市| 阳信县| 兰坪| 利津县| 金坛市| 永昌县| 永川市| 齐河县| 潞西市|