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

深入探究Java線程不安全的原因與解決

 更新時間:2022年04月26日 09:18:26   作者:淡沫初夏Zz  
線程不安全這個問題,一般在學(xué)Java時,我們老師會讓我們背誦一段長長的話。"當(dāng)不同線程同時能訪問同一個變量時,可能會導(dǎo)致線程不安全"。實(shí)際上,這句話重點(diǎn)想突出的只有原子性。而我們往往考慮線程不安全的原因,會從三方面進(jìn)行考慮:就是原子性,可見性,有序性

一、什么是線程安全

想給出一個線程安全的確切定義是復(fù)雜的,但我們可以這樣認(rèn)為:

如果多線程環(huán)境下代碼運(yùn)行的結(jié)果是符合我們預(yù)期的,即在單線程環(huán)境應(yīng)該的結(jié)果,則說這個程序是線程安全的

二、線程不安全的原因

1、修改共享數(shù)據(jù)

static class Counter {
    public int count = 0;
    void increase() {
        count++;
    }
}
    public static void main(String[] args) throws InterruptedException {
        final Counter counter = new Counter();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                counter.increase();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                counter.increase();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(counter.count);
}

上面的線程不安全的代碼中, 涉及到多個線程針對 counter.count 變量進(jìn)行修改.此時這個 counter.count 是一個多個線程都能訪問到的 “共享數(shù)據(jù)”

2、原子性

原子性就是 提供互斥訪問,同一時刻只能有一個線程對數(shù)據(jù)進(jìn)行操作,有時也把這個現(xiàn)象叫做同步互斥,表示操作是互相排斥的

不保證原子性會給多線程帶來什么問題 如果一個線程正在對一個變量操作,中途其他線程插入進(jìn)來了,如果這個操作被打斷了,結(jié)果就可能是錯誤的。 這點(diǎn)也和線程的搶占式調(diào)度密切相關(guān). 如果線程不是 “搶占” 的, 就算沒有原子性, 也問題不大

3、內(nèi)存可見性

可見性指, 一個線程對共享變量值的修改,能夠及時地被其他線程看到.

Java 內(nèi)存模型 (JMM): Java虛擬機(jī)規(guī)范中定義了Java內(nèi)存模型. 目的是屏蔽掉各種硬件和操作系統(tǒng)的內(nèi)存訪問差異,以實(shí)現(xiàn)讓Java程序在各種平臺下都能達(dá)到一致的并發(fā)效果.

   private static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (count == 0) {
            }
            System.out.println(Thread.currentThread().getName() +
                    "執(zhí)?完成");
        });
        t1.start();
        Scanner scanner = new Scanner(System.in);
        System.out.print("->");
        count = scanner.nextInt();
    }

4、指令重排序

一個線程觀察其他線程中的指令執(zhí)行順序,由于指令重排序,該觀察結(jié)果一般雜亂無序,(happens-before原則)。

編譯器對于指令重排序的前提

“保持邏輯不發(fā)生變化”. 這一點(diǎn)在單線程環(huán)境下比較容易判斷, 但是在多線程環(huán)境下就沒那么容易了, 多線程的代碼執(zhí)行復(fù)雜程度更高, 編譯器很難在編譯階段對代碼的執(zhí)行效果進(jìn)行預(yù)測, 因此激進(jìn)的重排序很容易導(dǎo)致優(yōu)化后的邏輯和之前不等價

三、解決線程安全方案

  • volatile解決內(nèi)存可見性和指令重排序

代碼在寫入 volatile 修飾的變量的時候:

改變線程?作內(nèi)存中volatile變量副本的值,將改變后的副本的值從?作內(nèi)存刷新到主內(nèi)存

  • 直接訪問工作內(nèi)存,速度快,但是可能出現(xiàn)數(shù)據(jù)不?致的情況
  • 加上 volatile , 強(qiáng)制讀寫內(nèi)存. 速度是慢了, 但是數(shù)據(jù)變的更準(zhǔn)確了

代碼示例:

/**
 * 內(nèi)存可見性
 * 線程1沒感受到flag的變化,實(shí)際線程2已經(jīng)改變了flag的值
 * 使用volatile,解決內(nèi)存可見性和指令重排序
 */
public class ThreadSeeVolatile {
    //全局變量
    private volatile static boolean flag = true;
    public static void main(String[] args) {
        //創(chuàng)建子線程
        Thread t1 = new Thread(() ->{
            System.out.println("1開始執(zhí)行:" + LocalDateTime.now());
            while(flag){
            }
            System.out.println("2結(jié)束執(zhí)行" + LocalDateTime.now());
        });
        t1.start();
        Thread t2 = new Thread(() ->{
            //休眠1s
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("修改flag=false"+ LocalDateTime.now());
            flag = false;
        });
        t2.start();
    }
}

volatile的缺點(diǎn)

volatile 雖然可以解決內(nèi)存可見性和指令重排序的問題,但是解決不了原子性問題,因此對于 ++ 和 --操作的線程非安全問題依然解決不了

  • 通過synchronized鎖實(shí)現(xiàn)原子性操作

JDK提供鎖分兩種:

①一種是synchronized,依賴JVM實(shí)現(xiàn)鎖,因此在這個關(guān)鍵字作用對象的作用范圍內(nèi)是同一時刻只能有一個線程進(jìn)行操作;

②另一種是LOCK,是JDK提供的代碼層面的鎖,依賴CPU指令,代表性的是ReentrantLock。

  • synchronized 會起到互斥效果, 某個線程執(zhí)行到某個對象的synchronized 中時, 其他線程如果也執(zhí)行到同一個對象 synchronized 就會阻塞等待.
  • 進(jìn)入 synchronized 修飾的代碼塊, 相當(dāng)于 加鎖
  • 退出 synchronized 修飾的代碼塊, 相當(dāng)于 解鎖

synchronized修飾的對象有四種:

(1)修飾代碼塊,作用于調(diào)用的對象

(2)修飾方法,作用于調(diào)用的對象

(3)修飾靜態(tài)方法,作用于所有對象

(4)修飾類,作用于所有對象

   // 修飾一個代碼塊: 明確指定鎖哪個對象
    public void test1(int j) {
        synchronized (this) {
        }
    }
    // 修飾一個方法
    public synchronized void test2(int j) {
    }
    // 修飾一個類
    public static void test1(int j) {
        synchronized (SynchronizedExample2.class) {
        }
    }
    // 修飾一個靜態(tài)方法
    public static synchronized void test2(int j) {
    }

到此這篇關(guān)于深入探究Java線程不安全的原因與解決的文章就介紹到這了,更多相關(guān)Java線程不安全內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java中SynchronousQueue的底層實(shí)現(xiàn)原理剖析

    Java中SynchronousQueue的底層實(shí)現(xiàn)原理剖析

    BlockingQueue的實(shí)現(xiàn)類中,有一種阻塞隊列比較特殊,就是SynchronousQueue(同步移交隊列),隊列長度為0。本文就來剖析一下SynchronousQueue的底層實(shí)現(xiàn)原理,感興趣的可以了解一下
    2022-11-11
  • java實(shí)戰(zhàn)案例之用戶注冊并發(fā)送郵件激活/發(fā)送郵件驗證碼

    java實(shí)戰(zhàn)案例之用戶注冊并發(fā)送郵件激活/發(fā)送郵件驗證碼

    現(xiàn)在很多的網(wǎng)站都提供有用戶注冊功能,當(dāng)我們注冊成功之后就會收到封注冊網(wǎng)站的郵件,郵件里包含了我們的注冊的用戶名和密碼及激活賬戶的超鏈接等信息,這篇文章主要給大家介紹了關(guān)于java實(shí)戰(zhàn)案例之用戶注冊并發(fā)送郵件激活/發(fā)送郵件驗證碼的相關(guān)資料,需要的朋友可以參考下
    2021-09-09
  • Java實(shí)現(xiàn)對視頻進(jìn)行截圖的方法【附ffmpeg下載】

    Java實(shí)現(xiàn)對視頻進(jìn)行截圖的方法【附ffmpeg下載】

    這篇文章主要介紹了Java實(shí)現(xiàn)對視頻進(jìn)行截圖的方法,結(jié)合實(shí)例形式分析了Java使用ffmpeg針對視頻進(jìn)行截圖的相關(guān)操作技巧,并附帶ffmpeg.exe文件供讀者下載使用,需要的朋友可以參考下
    2018-01-01
  • Springcloud+Mybatis使用多數(shù)據(jù)源的四種方式(小結(jié))

    Springcloud+Mybatis使用多數(shù)據(jù)源的四種方式(小結(jié))

    這篇文章主要介紹了Springcloud+Mybatis使用多數(shù)據(jù)源的四種方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java基于二分搜索樹、鏈表的實(shí)現(xiàn)的集合Set復(fù)雜度分析實(shí)例詳解

    Java基于二分搜索樹、鏈表的實(shí)現(xiàn)的集合Set復(fù)雜度分析實(shí)例詳解

    這篇文章主要介紹了Java基于二分搜索樹、鏈表的實(shí)現(xiàn)的集合Set復(fù)雜度分析,結(jié)合實(shí)例形式詳細(xì)分析了Java基于二分搜索樹、鏈表的實(shí)現(xiàn)的集合Set復(fù)雜度分析相關(guān)操作技巧與注意事項,需要的朋友可以參考下
    2020-03-03
  • Java的深拷貝和淺拷貝深入了解

    Java的深拷貝和淺拷貝深入了解

    這篇文章主要為大家介紹了Java的深拷貝和淺拷貝,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • 詳解Elasticsearch如何把一個索引變?yōu)橹蛔x

    詳解Elasticsearch如何把一個索引變?yōu)橹蛔x

    這篇文章主要為大家介紹了詳解Elasticsearch如何把一個索引變?yōu)橹蛔x示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • SpringBoot中的404錯誤:原因、影響及解決策略

    SpringBoot中的404錯誤:原因、影響及解決策略

    本文詳細(xì)介紹了SpringBoot中404錯誤的出現(xiàn)原因、影響以及處理策略,404錯誤常見于URL路徑錯誤、控制器配置問題、靜態(tài)資源配置錯誤、依賴缺失或版本不兼容、配置錯誤和服務(wù)器配置問題,解決方法包括檢查URL路徑、審查控制器配置、配置靜態(tài)資源
    2025-02-02
  • 老生常談Java中instanceof關(guān)鍵字的理解

    老生常談Java中instanceof關(guān)鍵字的理解

    java 中的instanceof 運(yùn)算符是用來在運(yùn)行時指出對象是否是特定類的一個實(shí)例。這篇文章主要介紹了老生常談Java中instanceof關(guān)鍵字的理解,需要的朋友可以參考下
    2018-10-10
  • Java?處理樹形結(jié)構(gòu)數(shù)據(jù)的過程

    Java?處理樹形結(jié)構(gòu)數(shù)據(jù)的過程

    這篇文章主要介紹了Java?處理樹形結(jié)構(gòu)數(shù)據(jù)的過程,本文給大家分析具體實(shí)現(xiàn)過程,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-08-08

最新評論

永寿县| 衡阳县| 云霄县| 玉林市| 石台县| 南昌市| 阜宁县| 延边| 丹寨县| 齐齐哈尔市| 广南县| 蒲江县| 夏邑县| 镇江市| 房山区| 马关县| 三门县| 毕节市| 双鸭山市| 乳源| 峡江县| 昭苏县| 祁东县| 西城区| 信宜市| 塔城市| 香河县| 临朐县| 铜川市| 浙江省| 蓝山县| 镇雄县| 马边| 焦作市| 阳山县| 德钦县| 延边| 寿阳县| 龙江县| 综艺| 杭锦后旗|