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

淺談java線程狀態(tài)與線程安全解析

 更新時間:2023年02月03日 09:25:39   作者:invictusQAQ  
本文主要介紹了淺談java線程狀態(tài)與線程安全解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

1.線程的幾種狀態(tài)

1.1 線程的狀態(tài)

以下就是我們線程所有的狀態(tài)和意義:

NEW已經(jīng)創(chuàng)建Thread但未創(chuàng)建線程
RUNNABLE可工作的. 又可以分成正在工作中和即將開始工作
BLOCKED等待鎖(阻塞狀態(tài))
WAITING調(diào)用wati方法(阻塞狀態(tài))
TIMED_WAITING調(diào)用sleep方法(阻塞狀態(tài))
TERMINATED系統(tǒng)線程執(zhí)行完畢已銷毀,但Thread還存在

注意:

BLOCKED 表示等待獲取鎖, WAITING 和 TIMED_WAITING 表示等待其他線程發(fā)來通知.

TIMED_WAITING 線程在等待喚醒,但設(shè)置了時限; WAITING 線程在無限等待喚醒 

1.2 線程狀態(tài)的轉(zhuǎn)移 

各線程之間的轉(zhuǎn)移關(guān)系可以簡化成下圖:

 關(guān)于yield方法:

在多線程中我們存在一個yield方法可以讓線程在就緒隊列中重新”排隊“,不改變線程狀態(tài)。相當于你去幫別人排隊,但是輪到你了那個人還沒回來,你就就讓原本排在你后面的人換到你的位置上,但你仍然處于排隊狀態(tài)。這種”大公無私“的行為可以類比到我們的yield方法幫助我們理解。

public class Demo{
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() { while (true) {
                System.out.println("張三");
                // 先注釋掉, 再放開
                //Thread.yield();
            }
            }
        }, "t1");
        t1.start();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("李四");
                }
            }
        }, "t2");
        t2.start();
 
    }
}

 可以看到:

1. 不使用 yield 的時候, 張三李四大概五五開

2. 使用 yield 時, 張三的數(shù)量遠遠少于李四

結(jié)論: yield 不改變線程的狀態(tài), 但是會重新去排隊.

2.有關(guān)線程安全問題

2.1 一個簡單的例子

// 創(chuàng)建兩個線程, 讓這倆線程同時并發(fā)的對一個變量, 自增 5w 次. 最終預期能夠一共自增 10w 次.
class Counter {
    // 用來保存計數(shù)的變量
    public int count;
 
    public void increase() {
        count++;
    }
}
 
public class Demo {
    // 這個實例用來進行累加.
    // public static Counter counter = new Counter();
 
    public static void main(String[] args) {
        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();
 
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("count: " + counter.count);
    }
}

大家先看到以上的代碼,意思很簡單,用兩個線程對同一個變量進行自增操作,運行的結(jié)果如下 

看起來不太對,我們再試一次

結(jié)果有了變化,但仍然不是我們想要的結(jié)果,是什么導致了5w+5w<10w呢?其中一個原因就是線程的隨機調(diào)度和改操作不具有原子性。 這些概念我們下面會詳細講,這里我們先簡單了解一下。

首先我們的自增操作在cpu內(nèi)其實分為三步:

1.LOAD:cpu從內(nèi)存中讀取數(shù)據(jù)到寄存器

2.ADD:在寄存器內(nèi)實現(xiàn)自增

3.SAVE:將寄存器的數(shù)據(jù)寫回內(nèi)存中

而我們已經(jīng)知道cpu對于線程調(diào)度我們可以理解為是隨機的,所以會有很多種可能,比如下圖

其中縱軸代表運行時間,這里我們可以看到兩個線程相當于互不影響,線程1完成自增操作后又將數(shù)據(jù)寫回內(nèi)存由線程2再去操作,這種情況下是沒有問題的。但是也可能是下面的一種情況

 此時線程1還沒有將自增后的數(shù)據(jù)寫回內(nèi)存而線程2就已經(jīng)將要修改的數(shù)據(jù)讀入了寄存器,此時相當于線程2讀到了那個還未自增的數(shù)據(jù),相當于兩個線程對同一個數(shù)進行了自增,所以此時相當于只自增了一次。其實情況還有很多,這里我們僅舉例比較經(jīng)典的例子。所以這也能夠解釋為什么結(jié)果大于5w而小于10w了。

2.2 造成線程不安全的原因

2.2.1 操作系統(tǒng)的隨機調(diào)度/搶占式運行

這種是操作系統(tǒng)內(nèi)核就已經(jīng)決定的,我們無能為力。類似于我們上一個例子,就是因為線程的隨機調(diào)度和操作不具有原子性造成的。 

2.2.2 操作不具有原子性 

什么是原子性

我們把一段代碼想象成一個房間,每個線程就是要進入這個房間的人。如果沒有任何機制保證,A進入 房間之后,還沒有出來;B 是不是也可以進入房間,打斷 A 在房間里的隱私。這個就是不具備原子性 的。轉(zhuǎn)換成代碼我們可以理解成只具有一條指令的操作。

當然這個問題我們可以通過加鎖操作解決(以后會提到)。

一條 java 語句不一定是原子的,也不一定只是一條指令,比如我們上面提到的自增操作。

不保證原子性會給多線程帶來什么問題

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

2.2.3 多個線程修改同一個變量

1.一個線程修改變量沒事

2.多個線程同時一個變量也沒事

3.多個線程同時修改不同變量也沒有問題

唯獨需要注意多個線程修改同一個變量,如果不加以處理可能會造成我們之前講到的例子的問題 

2.2.4 內(nèi)存可見性問題 

jvm中規(guī)定了java的內(nèi)存模型

線程之間的共享變量存在 主內(nèi)存 (Main Memory).

每一個線程都有自己的 "工作內(nèi)存" (Working Memory) .

當線程要讀取一個共享變量的時候, 會先把變量從主內(nèi)存拷貝到工作內(nèi)存, 再從工作內(nèi)存讀取數(shù)據(jù).

當線程要修改一個共享變量的時候, 也會先修改工作內(nèi)存中的副本, 再同步回主內(nèi)存. 

正是因為這種機制,所以可能會出現(xiàn)下面的問題:

由于每個線程有自己的工作內(nèi)存, 這些工作內(nèi)存中的內(nèi)容相當于同一個共享變量的 "副本". 此時修改線程 1 的工作內(nèi)存中的值, 線程2 的工作內(nèi)存不一定會及時變化.通俗的講就是 線程1針對工作內(nèi)容修改了數(shù)據(jù),而線程2此時并不一定能夠及時同步修改的數(shù)據(jù),所以可能會引發(fā)各種問題。

2.2.5 指令重排序 

所謂指令重排序是指jvm針對我們的代碼,可能會在保證邏輯不變的情況下去調(diào)整指令執(zhí)行的順序以達到運行效率更高的效果。這種情況在單線程的情況下可以很好實現(xiàn),而在多線程的情況下就可能會出現(xiàn)bug,導致程序邏輯改變。比如對于下面這行代碼:

Test t=new Test();

它其實總共有三個步驟:

1.創(chuàng)建內(nèi)存空間

2.往這個內(nèi)存空間構(gòu)造一個對象

3.將這個內(nèi)存引用賦給t 

在單線程的情況下2,3互換并不會有上面影響,但假如在多線程情況下我們按1,3,2來執(zhí)行,當執(zhí)行到3時t為非null,此時線程2讀取t,但是卻發(fā)現(xiàn)是一個無效對象。 

到此這篇關(guān)于淺談java線程狀態(tài)與線程安全解析的文章就介紹到這了,更多相關(guān)java線程狀態(tài)與線程安全內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JAVA關(guān)鍵字及作用詳解

    JAVA關(guān)鍵字及作用詳解

    本文主要介紹了Java關(guān)鍵字及作用,具有很好的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • JavaWeb項目中JSP訪問的問題解決

    JavaWeb項目中JSP訪問的問題解決

    JSP文件一般有兩個存放位置,本文主要介紹了JavaWeb項目中JSP訪問的問題解決,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • SpringBoot3集成Thymeleaf的過程詳解

    SpringBoot3集成Thymeleaf的過程詳解

    在現(xiàn)代的Web開發(fā)中,構(gòu)建靈活、動態(tài)的用戶界面是至關(guān)重要的,Spring Boot和Thymeleaf的結(jié)合為開發(fā)者提供了一種簡單而強大的方式來創(chuàng)建動態(tài)的Web應用,本文將介紹如何在Spring Boot項目中集成Thymeleaf,并展示一些基本的使用方法,需要的朋友可以參考下
    2024-01-01
  • 詳解java集成支付寶支付接口(JSP+支付寶20160912)

    詳解java集成支付寶支付接口(JSP+支付寶20160912)

    本篇文章主要介紹了java集成支付寶支付接口,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-12-12
  • 深入了解Java線程池的原理使用及性能優(yōu)化

    深入了解Java線程池的原理使用及性能優(yōu)化

    JAVA線程池是一種管理和復用線程資源的機制,可以提高程序的效率和響應速度。本文將介紹線程池的原理、使用方法和性能優(yōu)化技巧,幫助讀者深入了解和應用JAVA線程池
    2023-04-04
  • Java技巧函數(shù)方法實現(xiàn)二維數(shù)組遍歷

    Java技巧函數(shù)方法實現(xiàn)二維數(shù)組遍歷

    這篇文章主要介紹了Java技巧函數(shù)方法實現(xiàn)二維數(shù)組遍歷,二維數(shù)組遍歷,每個元素判斷下是否為偶數(shù),相關(guān)內(nèi)容需要的小伙伴可以參考一下
    2022-08-08
  • 關(guān)于Spring源碼深度解析(AOP功能源碼解析)

    關(guān)于Spring源碼深度解析(AOP功能源碼解析)

    這篇文章主要介紹了關(guān)于Spring源碼深度解析(AOP功能源碼解析),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Java中的CopyOnWriteArrayList原理詳解

    Java中的CopyOnWriteArrayList原理詳解

    這篇文章主要介紹了Java中的CopyOnWriteArrayList原理詳解,如源碼所示,CopyOnWriteArrayList和ArrayList一樣,都在內(nèi)部維護了一個數(shù)組,操作CopyOnWriteArrayList其實就是在操作內(nèi)部的數(shù)組,需要的朋友可以參考下
    2023-12-12
  • MyBatis使用雪花ID的實現(xiàn)

    MyBatis使用雪花ID的實現(xiàn)

    本文主要介紹了MyBatis使用雪花ID的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-04-04
  • Java匿名內(nèi)部類的寫法示例

    Java匿名內(nèi)部類的寫法示例

    這篇文章主要給大家介紹了關(guān)于Java匿名內(nèi)部類的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-08-08

最新評論

黑山县| 梁河县| 珠海市| 韶山市| 曲松县| 柯坪县| 家居| 济宁市| 宜城市| 宜都市| 澄城县| 运城市| 肥乡县| 神池县| 成安县| 高淳县| 正蓝旗| 金秀| 吴堡县| 京山县| 台东县| 小金县| 邵武市| 武威市| 东莞市| 喀喇沁旗| 新民市| 图木舒克市| 内丘县| 依兰县| 英德市| 镶黄旗| 富锦市| 灵台县| 台中县| 姜堰市| 丰原市| 民县| 会理县| 三江| 新巴尔虎左旗|