欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java線程安全之volatile詳解

 更新時間:2023年08月26日 09:19:42   作者:not coder  
這篇文章主要介紹了Java線程安全之volatile詳解,volatile 的存在,解決了不同內存間拷貝的同步問題,在每一次使用或者修改時候,都去原持有內存中去拿最新的狀態(tài),需要的朋友可以參考下

volatile

volatile 的存在,解決了不同內存間拷貝的同步問題,在每一次使用或者修改時候,都去原持有內存中去拿最新的狀態(tài)

或者說可以這樣理解,volatile 的修飾讓線程放棄了使用各自內存的做法轉而使用共享內存,從而保證了可靠性,但是降低了效率

所以說我們只在需要不同線程訪問同一個值的時候才去打開這個限制

第一種情況

首先我們來寫一個測試代碼

public class Synchronized1Test {
    private boolean running = true;
    public void runTest() {
        new Thread() {
            @Override
            public void run() {
                while (running) {
                    System.out.println("線程開始執(zhí)行,,,,");
                }
            }
        }.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        stop();
    }
    private void stop() {
        running = false;
    }
}

代碼大致要表達的是,主線程在一秒之后要調用 stop 方法將 running = false,這樣,子線程的死循環(huán)就會被打破,這樣子線程停止執(zhí)行,主線程也執(zhí)行完畢,整個程序結束。

但是實際的執(zhí)行結果是,子線程的死循環(huán)并不會結束,會一直不停的執(zhí)行下去

這是因為線程直接做變量的值的修改并不是直接改的,而是先拷貝一份到自己線程的內存區(qū)域,更改完之后再適時同步給原持有此值的線程,這樣做的目的是提高程序的運行效率

而在這里,主線程持有此變量,子線程在執(zhí)行時候,將值拷貝了一份到自己的內存中,然后子線程并沒有做值的修改,并不存在將值同步給主線程的過程,而此值的持有線程是主線程,主線程在更改完此值之后只會同步給自己,并不會同步給子線程,所以說子線程的值永遠為最初拷貝走的值,永遠為true,就永遠不會停下來了。

有一個關鍵字 volatile 能解決這個問題

public class Synchronized1Test {
    private volatile boolean running = true;
	.....
}

volatile 的存在,解決了不同內存間拷貝的同步問題,在每一次使用或者修改時候,都去原持有內存中去拿最新的狀態(tài),或者說可以這樣理解,volatile 的修飾讓線程放棄了使用各自內存的做法轉而使用共享內存,從而保證了可靠性,但是降低了效率,所以說我們只在需要不同線程訪問同一個值的時候才去打開這個限制

再次運行,果然一秒結束

第二種情況

public class Synchronized2Test {
    private int x = 0;
    private void count() {
        x++;
    }
    public void runTest() {
        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 1_000_000; i++) {
                    count();
                }
                System.out.println("x 在線程1的最終值" + x);
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 1_000_000; i++) {
                    count();
                }
                System.out.println("x 在線程2的最終值" + x);
            }
        }.start();
    }
}

從代碼來看,兩個線程分別對同一個數進行一百萬次從自增操作,無論誰最后執(zhí)行完成,理論上來講,總有一個線程打印出來的結果是兩百萬,但實際的運行效果,無論執(zhí)行多少次都沒有兩百萬的結果出來。

都是因為上文說的,值的同步性問題,在各自內存區(qū)域修改完后適時同步回去,導致的這個問題

然后我們加上 volatile 關鍵字

public class Synchronized2Test {
    private volatile int x = 0;
	.....
}

運行,還是不行,為啥啊,都加上了怎么還是不行呢?

這里主要的原因是因為 x++; 這個操作

在 Java 中, x++; 是兩步操作,不是原子操作,是可拆的操作

他的運算過程大致相當于是

int temp = x + 1;
	x = temp;

這樣,在代碼分為兩行執(zhí)行的時候,搶線程的操作就發(fā)生了,就出現了值異常的情況

到目前為止,線程安全除了加 volatile 關鍵字外,還需要保證會互相影響的操作合成一個原子操作

這里解釋一下原子操作,簡單來說,把多個步驟看成一個整體,要么你別執(zhí)行我,要么就完全執(zhí)行,不要在執(zhí)行一半時候發(fā)生線程切換

這里引入我們保證一塊代碼塊執(zhí)行原子操作的關鍵字:synchronized

這時候我們改一下我們的代碼,給 count 方法增加 synchronized 關鍵字,并且可以將 volatile 關鍵字去除了

public class Synchronized2Test {
    private int x = 0;
    private synchronized void count() {
        x++;
    }
    .....
}

運行,果然順利的打印出來了兩百萬,多次運行也是這個結果

受保護的類型

到此,我們講一下官方提供的原子型的類型,這里拿之前出現過的 AtomicInteger 舉例

AtomicInteger 對標的就是 int,他是對 int 的包裝,可以保證 int 具有同步性和原子性

這里的同步性可以理解為上文提到的被 volatile 修飾,原子性可以理解為被 synchronized 修飾

例:

// 可以理解為默認值為 0 的 int
AtomicInteger count  = new AtomicInteger(0);
// 相當于是 ++count
count.incrementAndGet();
// 相當于是 count++
count.getAndIncrement();

代碼中的 count.incrementAndGet() 相當于是 ++count

count.getAndIncrement() 相當于是 count++

除此之外還有

常規(guī)類型保護類型
intAtomicInteger
booleanAtomicBoolean
int[]AtomicIntegerArray
TAtomicReference< T>

不一一列舉

其中要說的就是 AtomicReference,他把傳入的范型,自動幫我們做成了保護類型,使得在賦值和取值時候不出錯

到此這篇關于Java線程安全之volatile詳解的文章就介紹到這了,更多相關Java的volatile內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 實例講解Java中動態(tài)代理和反射機制

    實例講解Java中動態(tài)代理和反射機制

    在本篇文章里小編給各位分享了關于Java中動態(tài)代理和反射機制的相關知識點內容,有需要的朋友們學習下。
    2019-01-01
  • 你知道在Java中Integer和int的這些區(qū)別嗎?

    你知道在Java中Integer和int的這些區(qū)別嗎?

    最近面試,突然被問道,說一下Integer和int的區(qū)別.額…可能平時就知道寫一些業(yè)務代碼,包括面試的一些Spring源碼等,對于這種特別基礎的反而忽略了,導致面試的時候突然被問到反而不知道怎么回答了.哎,還是乖乖再看看底層基礎,順帶記錄一下把 ,需要的朋友可以參考下
    2021-06-06
  • Spring Boot示例代碼整合Redis詳解

    Spring Boot示例代碼整合Redis詳解

    SpringBoot對常用的數據庫支持外,對NoSQL 數據庫也進行了封裝自動化,下面這篇文章主要給大家介紹了關于springboot使用redis的詳細步驟,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-06-06
  • SpringBoot項目中集成Apollo的方法步驟

    SpringBoot項目中集成Apollo的方法步驟

    本文主要介紹了SpringBoot項目中集成Apollo的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-10-10
  • 詳細講解Java的泛型

    詳細講解Java的泛型

    這篇文章主要介紹了Java的泛型,是Java入門學習中的基礎知識,需要的朋友可以參考下
    2015-09-09
  • 詳解Java中實現SHA1與MD5加密算法的基本方法

    詳解Java中實現SHA1與MD5加密算法的基本方法

    這篇文章主要介紹了詳解Java中實現SHA1與MD5加密算法的基本方法,安全哈希算法第一版和消息摘要算法第五版也是通常人們最常用的加密算法,需要的朋友可以參考下
    2016-04-04
  • Spring線程池ThreadPoolExecutor配置并且得到任務執(zhí)行的結果

    Spring線程池ThreadPoolExecutor配置并且得到任務執(zhí)行的結果

    今天小編就為大家分享一篇關于Spring線程池ThreadPoolExecutor配置并且得到任務執(zhí)行的結果,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • SpringBoot和MybatisPlus實現通用Controller示例

    SpringBoot和MybatisPlus實現通用Controller示例

    本文主要介紹了SpringBoot和MybatisPlus實現通用Controller示例,只需創(chuàng)建實體類和mapper接口,就可以實現單表的增刪改查操作,具有一定的參考價值,感興趣的可以了解一下
    2025-03-03
  • 一文帶你了解Java排序算法

    一文帶你了解Java排序算法

    這篇文章主要為大家詳細介紹了Java中常見的三個排序算法:選擇排序,冒泡排序和插入排序,文中的示例代碼講解詳細,感興趣的可以了解一下
    2022-08-08
  • SpringMVC實現文件上傳和下載的工具類

    SpringMVC實現文件上傳和下載的工具類

    這篇文章主要為大家詳細介紹了SpringMVC實現文件上傳和下載的工具類,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-05-05

最新評論