国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Java并發(fā)編程-volatile

這篇具有很好參考價值的文章主要介紹了Java并發(fā)編程-volatile。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

volatilejava虛擬機提供的一種輕量級的同步機制,它有三個重要的特性:

  • 保證可見性
  • 不保證原子性
  • 禁止指令重排

要理解這三個特性,就需要對JMM(JAVA內(nèi)存模型)有一定的了解才行。

主要解決的問題:
JVM中,每個線程都會存在本地內(nèi)存,本地內(nèi)存是公共內(nèi)存的副本,各個線程的本地內(nèi)存相互隔離,就會存在一個線程對共享變量做了修改,其他線程沒有感知到的情況,從而導(dǎo)致數(shù)據(jù)不一致

一、JMM(JAVA內(nèi)存模型)

JMMJava 虛擬機規(guī)范中所定義的一種內(nèi)存模型,Java 內(nèi)存模型是標準化的,屏蔽掉了底層不同計算機的區(qū)別。也就是說,JMMJVM 中定義的一種并發(fā)編程的底層模型機制。JMM定義了線程和主內(nèi)存(可以理解為買電腦時8/16G內(nèi)存)之間的抽象關(guān)系:不同線程之間的共享變量存在主內(nèi)存中,而每個線程中存在一個私有的本地內(nèi)存,對共享變量的操作需要將主內(nèi)存中的共享變量拷貝一份到本地內(nèi)存中。也就是說,在每個線程的本地內(nèi)存中存在的是共享變量的副本。

JMM關(guān)于同步的規(guī)定:

  • 1、線程解鎖前,必須把共享變量的值刷新會主內(nèi)存
  • 2、線程加鎖前,必須讀取主內(nèi)存中的最新共享變量的值到本地內(nèi)存
  • 3、加解鎖是同一把鎖

每個線程在創(chuàng)建時JVM都會為其分配工作內(nèi)存(也叫??臻g),工作內(nèi)存是每個線程的私有區(qū)域。而java內(nèi)存模型規(guī)定所有變量都必須存在主內(nèi)存中,主內(nèi)存是共享區(qū)域,所有線程都可以訪問。但是線程對變量的操作必須在工作內(nèi)存中進行,大概流程就是,線程將變量的值從主內(nèi)存拷貝到本地內(nèi)存中,進行操作,然后在將其寫回主內(nèi)存。由于不同線程之間的工作內(nèi)存互不可見,所有線程中的通信必須通過主內(nèi)存來進行。具體過程如下:

由于JMM這樣的機制,就導(dǎo)致了可見性的問題。

JMM三大特性

  • 可見性
  • 原子性
  • 有序性

二、可見性

內(nèi)存可見性指當一個線程修改了某個變量的值后,其他線程總能知道這個值的變化。

這里用例子來說明一下:

package com.fzkj.juc;

import java.util.concurrent.TimeUnit;

/**
 * @DESCRIPTION  volatile關(guān)鍵字測試類
 */
public class VolatileTest {

    public static void main(String[] args) {
        Number number = new Number();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            number.numTo(20);
            System.out.println(Thread.currentThread().getName() + ":\t number的值是: " + number.num);
        }, "A線程").start();

        while(number.num == 0){}

        System.out.println(Thread.currentThread().getName() + ":\t number的值是: " + number.num);
    }


}

class Number{
    int num = 0;

    public void numTo(int target){
        this.num = target;
    }

    public void add(){
        this.num++ ;
    }
}

運行上面的例子就會發(fā)現(xiàn),程序會陷入死循環(huán),永遠不會輸出最后一句話。就是因為A線程中對變量num的修改對main線程不可見,導(dǎo)致while循環(huán)一直進行。

可見性問題常見的解決方案包括:

  • 加鎖
  • volatile關(guān)鍵字

volatile

對上面代碼進行改造

class Number{
    volatile int num = 0;

    public void numTo(int target){
        this.num = target;
    }

    public void add(){
        this.num++ ;
    }
}

這樣在運行上面例子。就不會在陷入死循環(huán)了。

volatile是如何保證可見性的?

其他線程又是如何知道共享變量被修改了呢?
為了解決緩存一致性問題,需要遵循一些協(xié)議,叫做緩存一致性協(xié)議,如:MSI、MESI(IllinoisProtocol)、MOSI、Synapse、Firefly及DragonProtocol等。

嗅探

通過嗅探機制來保證及時的知道自己的緩存過期了。
在多處理器下,為了保證各個處理器的緩存是一致的,就會實現(xiàn)緩存一致性協(xié)議,每個處理器通過嗅探在總線上傳播的數(shù)據(jù)來檢查自己緩存的值是不是過期了,
當處理器發(fā)現(xiàn)自己緩存行對應(yīng)的內(nèi)存地址被修改,就會將當前處理器的緩存行設(shè)置成無效狀態(tài),當處理器對這個數(shù)據(jù)進行修改操作的時候,會重新從系統(tǒng)內(nèi)存中把數(shù)據(jù)讀到處理器緩存里

由于嗅探機制會不斷的監(jiān)聽總線,打量使用volatile可能會引起總線風暴

三、原子性

在來看另一種情況。

package com.fzkj.juc;

import java.util.concurrent.TimeUnit;

/**
 * @DESCRIPTION  volatile關(guān)鍵字測試類
 */
public class VolatileTest {

    public static void main(String[] args) {
        atomicity();
    }

    // 原子性
    public static void atomicity(){
        Number num = new Number();
        for (int i = 0; i < 10; i++) { // 啟動10個線程
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) { // 每個線程對num的值操作1000次
                    num.add();
                }
            }).start();
        }
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(num.num);
    }
}

class Number{
     int num = 0;

    public void numTo(int target){
        this.num = target;
    }

    public void add(){
        this.num++ ;
    }
}

在上面的例子中,我們啟動了10個線程,每個線程調(diào)用了1000次add方法,對num的值進行1000累加,那么我們期待的最終結(jié)果就是num的值是10000。但是實際上運行程序就會發(fā)現(xiàn),每次的結(jié)果都會比10000少。

這個問題的成因,其實跟jvm有關(guān)系,我們都知道,程序員寫的代碼只是給程序員自己看的,還需要將代碼編譯才是機器執(zhí)行的。一個++操作被編譯成字節(jié)碼文件之后,可以簡化成三個步驟。第一步取值;第二步加一;第三步賦值。所以在高并發(fā)的場景下,就會出現(xiàn)值被覆蓋的情況。

原子性的定義:指在一組操作中,要么全部操作都成功,要么全部操作都失敗。

原子性是JMM的特性之一,但是volatile卻并不支持原子性。要想在多線程的環(huán)境下保證原子性,可以使用鎖機制,或者使用原子類(AtomicInteger)。

四、有序性

禁止指令重排就叫做有序性。

什么是指令重排?

為了提高性能,在遵守as-if-serial語義的情況下,編譯器和處理器往往會對指令做重排序。在多線程的情況下,指令重排可能會導(dǎo)致一些意想不到的情況。

volatile是怎么禁止指令的重排序的呢?這里又引出一個新的概念:內(nèi)存屏障

內(nèi)存屏障

內(nèi)存屏障的作用是禁止指令重排序和解決內(nèi)存可見性的問題。

先了解兩個指令:

  • store:將緩存中的數(shù)據(jù)刷新到內(nèi)存中
  • load:將內(nèi)存存儲的數(shù)據(jù)拷貝到緩存中

JMM主要將內(nèi)存屏障分為四類

屏障類型 指令示例 說明
LoadLoad Load1;LoadLoad;Load2 確保Load1數(shù)據(jù)的裝載先于Load2
StoreStore Store1;StoreStore;Store2 確保Store1立刻刷新數(shù)據(jù)到內(nèi)存的操作先于Store2
LoadStore Load1;LoadStore;Store2 確保Load1數(shù)據(jù)裝載先于Store2數(shù)據(jù)刷新
StoreLoad Store1StoreLoad;Load2 確保Store1數(shù)據(jù)刷新先于Load2數(shù)據(jù)裝載

StoreLoad被稱為全能屏障,因其同時具備其他三個屏障的效果,但是相對于其他屏障,消耗會多。

了解了這些,下面就來看看volatile是如何插入內(nèi)存屏障的。

可以看到,

  • volatile在讀操作后面加了LoadLoad和LoadStore屏障
  • 在寫操作前后分別加了StoreStore和StoreLoad屏障

這就是說,編譯器不會對volatile讀和讀后面的操作重排序;不會對寫和寫前面的操縱重排序。這樣就保證了volatile本身的有序性。文章來源地址http://www.zghlxwxcb.cn/news/detail-738802.html

到了這里,關(guān)于Java并發(fā)編程-volatile的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • Java 并發(fā)之《深入理解 JVM》關(guān)于 volatile 累加示例的思考

    Java 并發(fā)之《深入理解 JVM》關(guān)于 volatile 累加示例的思考

    在周志明老師的 《深入理解 JVM》一書中關(guān)于 volatile 線程安全性有一個示例代碼(代碼有些許改動,語義一樣): 老師的目的是為了說明在多線程環(huán)境下 volatile 只能保證可見性而不是線程安全的。但是當在我的 IDEA 下運行時,發(fā)現(xiàn)程序是沒有輸出結(jié)果且始終是

    2024年01月20日
    瀏覽(19)
  • Tomcat 配合虛擬線程,一種新的編程體驗

    Tomcat 配合虛擬線程,一種新的編程體驗

    Java 21 在今年早些時候的 9 月 19 日就正式發(fā)布,并開始正式引入虛擬線程,但是作為 Java 開發(fā)生態(tài)中老大哥 Spring 并沒有立即跟進,而是在等待了兩個月后的 11 月 29 日,伴隨著 Spring Boot 3.2 版本的發(fā)布,在這個版本中也終于是引入了對虛擬線程的支持。 虛擬線程的引入標志著

    2024年02月05日
    瀏覽(14)
  • java高并發(fā)系列 - 第34篇:google提供的一些好用的并發(fā)工具類

    java高并發(fā)系列第34篇。 環(huán)境:jdk1.8。 關(guān)于并發(fā)方面的,juc已幫我們提供了很多好用的工具,而谷歌在此基礎(chǔ)上做了擴展,使并發(fā)編程更容易,這些工具放在guava.jar包中。 本文演示幾個簡單的案例,見一下guava的效果。 需要先了解的一些技術(shù):juc中的線程池、Excecutors、Execu

    2024年02月16日
    瀏覽(51)
  • Spring是一個開源的Java開發(fā)框架,它提供了一種快速、簡單的方式來開發(fā)企業(yè)級應(yīng)用程序

    Spring是一個開源的Java開發(fā)框架,它提供了一種快速、簡單的方式來開發(fā)企業(yè)級應(yīng)用程序

    Spring是一個開源的Java開發(fā)框架,它提供了一種快速、簡單的方式來開發(fā)企業(yè)級應(yīng)用程序。Spring的主要優(yōu)點包括簡化Java EE開發(fā)、提供依賴注入和面向切面編程等功能。以下是Spring的一些核心特性: 依賴注入(DI):Spring通過DI機制,將對象的依賴關(guān)系注入到應(yīng)用程序中,簡化了

    2024年02月03日
    瀏覽(98)
  • 【java】開發(fā)——《并發(fā)編程》

    【java】開發(fā)——《并發(fā)編程》

    目錄 一.jmm 二.并發(fā)了什么 1.只有一個核(單核)并發(fā)還有沒有意義 2.單核,還有什么可見性問題 3.并發(fā)和并行 三.volitaile 1.變量的可見性問題 2.原因是什么 3.本次修改的變量直接刷到主內(nèi)存 4.聲明其他內(nèi)存對于這個地址的緩存無效 四.happens-befo 1.順序性問題 五.volitaile+cas 1.原

    2024年02月22日
    瀏覽(26)
  • Java并發(fā)編程實戰(zhàn)

    2023年06月19日
    瀏覽(20)
  • Java并發(fā)編程:Semaphore

    信號量可以控制線程的并發(fā)數(shù)量 通常用于那些資源有明確訪問數(shù)量限制的場景,常用于限流 。 使用 Semaphore 先調(diào)用 acquire() 獲取,然后通過 try ... finally 保證在 finally 中釋放。 Semaphore常用方法說明 acquire() 獲取一個令牌,在獲取到令牌、或者被其他線程調(diào)用中斷之前線程一直

    2024年02月12日
    瀏覽(16)
  • Java-并發(fā)編程-進階篇

    Java-并發(fā)編程-進階篇

    在上一篇幅中對并發(fā)編程進行了簡單介紹:并發(fā)與并行,進程與線程,以及并發(fā)編程的簡單代碼 但是在企業(yè)中往往并不能解決實際問題,例如: 1.synchronized在企業(yè)開發(fā)中會大大降低系統(tǒng)的性能,有什么解決方式,或者其他的替代方案 2.當線程被創(chuàng)建并啟動以后,它既不

    2024年02月06日
    瀏覽(29)
  • Java并發(fā)編程面試題

    Java并發(fā)編程面試題

    目錄 一、線程、進程、程序 二、線程狀態(tài) ?三、線程的七大參數(shù) 四、線程有什么優(yōu)缺點? 五、start 和 run 方法有什么區(qū)別? 六、wait 和 sleep的區(qū)別? 七、lock與synchronized的區(qū)別 八、Volatile是線程安全的嗎?底層原理是什么? 九、synchronized作用和底層原理? 十一、Thre

    2024年02月12日
    瀏覽(25)
  • 【并發(fā)編程】深入理解Java并發(fā)之synchronized實現(xiàn)原理

    【并發(fā)編程】深入理解Java并發(fā)之synchronized實現(xiàn)原理

    分析: 通過 new MyThread() 創(chuàng)建了一個對象 myThread ,這時候堆中就存在了共享資源 myThread ,然后對 myThread 對象創(chuàng)建兩個線程,那么thread1線程和thread2線程就會共享 myThread 。 thread1.start() 和 thead2.start() 開啟了兩個線程,CPU會隨機調(diào)度這兩個線程。假如 thread1 先獲得 synchronized 鎖,

    2024年02月04日
    瀏覽(26)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包