Java volatile關(guān)鍵字特性講解上篇
一、概述
volatile是Java中的關(guān)鍵字,用來修飾會被不同線程訪問和修改的變量。
volatile是Java虛擬機(jī)提供的輕量級的同步機(jī)制,它有三個特性:
(1)保證可見性
(2)不保證原子性
(3)禁止指令重排
二、特性詳解
volatile保證可見性
Java內(nèi)存模型(JMM)定義了一組規(guī)則、規(guī)范,規(guī)定了程序中各個變量的訪問方法。JMM關(guān)于同步的規(guī)定:
(1)線程解鎖前,必須把共享變量的值刷新回主內(nèi)存;
(2)線程加鎖前,必須讀取主內(nèi)存的最新值同步到自己的工作內(nèi)存;
(3)加鎖解鎖必須是同一把鎖;
說明:由于JVM運行程序的實體是線程,創(chuàng)建每個線程時,JMM會為其創(chuàng)建一個工作內(nèi)存(也稱??臻g),工作內(nèi)存是每個線程的私有數(shù)據(jù)區(qū)域。
Java內(nèi)存模型規(guī)定所有變量都存儲在主內(nèi)存,主內(nèi)存是共享內(nèi)存區(qū)域,所有線程都可以訪問。
但是線程對變量的操作(讀取、賦值等)必須在工作內(nèi)存中進(jìn)行。因此首先要將變量從主內(nèi)存拷貝到自己的工作內(nèi)存,然后對變量進(jìn)行操作,操作完成后再將變量寫會主內(nèi)存中。
舉例說明:
(1)火車票賣票系統(tǒng)還剩下一張票,并已經(jīng)刷入到主內(nèi)存中:ticketNum = 1;
(2)此時有3個用戶在同時購買票,3個線程都讀入了目前的票數(shù),ticketNum=1,那么線程就會繼續(xù)進(jìn)入購買流程。
(3)假設(shè)其中一個線程先搶占了CPU資源,先買到票,并將自己的工作內(nèi)存中的ticketNum值改為0,ticketNum=0,然后再寫回到主內(nèi)存。
這時,由于一個線程的用戶已經(jīng)買到了票,那么其他用戶的線程應(yīng)該不能再繼續(xù)進(jìn)入購買票的流程了,因此需要系統(tǒng)通知到其他線程 ticketNum=0 這個消息。如果可以達(dá)到這樣的效果,可以理解為 具有可見性。
無可見性代碼演示:
@Test public void test1() { DataDemo dataDemo = new DataDemo(); RunThread runThread = new RunThread(dataDemo); runThread.start(); while (dataDemo.getNumber() == 0) { } System.out.println("具有可見性驗證通過"); } public class DataDemo { private int number = 0; public void add() { this.number = this.number + 10; } public int getNumber() { return number; } } public class RunThread extends Thread { private DataDemo dataDemo; public RunThread(DataDemo dataDemo) { this.dataDemo = dataDemo; } @Override public void run() { System.out.println("線程[" + Thread.currentThread().getName() + "] 正在執(zhí)行"); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } dataDemo.add(); System.out.println("線程[" + Thread.currentThread().getName() + "]更新后,number值為:" + dataDemo.getNumber()); } }
執(zhí)行結(jié)果:
線程[Thread-0] 正在執(zhí)行
線程[Thread-0]更新后,number值為:10
結(jié)果分析:
可以看出子線程啟動后將number值改為了10,雖然已經(jīng)改為了非0,但是主線程仍然一直處于while循環(huán)中,因此此時number不具有可見性,系統(tǒng)不會主動通知主線程number值修改。
原理說明:
這個問題其實就是私有堆棧中的值和公共堆棧中的值不同步造成的。解決這樣的問題就要使用 volatile 關(guān)鍵字了,它主要的作用就是當(dāng)線程訪問number這個變量時,強制性從公共堆棧中進(jìn)行取值。
可見性代碼演示:
@Test public void test1() { DataDemo dataDemo = new DataDemo(); RunThread runThread = new RunThread(dataDemo); runThread.start(); while (dataDemo.getNumber() == 0) { } System.out.println("具有可見性驗證通過"); } public class DataDemo { // 給變量 number 添加 volatile 關(guān)鍵字修飾 volatile private int number = 0; public void add() { this.number = this.number + 10; } public int getNumber() { return number; } } public class RunThread extends Thread { private DataDemo dataDemo; public RunThread(DataDemo dataDemo) { this.dataDemo = dataDemo; } @Override public void run() { System.out.println("線程[" + Thread.currentThread().getName() + "] 正在執(zhí)行"); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } dataDemo.add(); System.out.println("線程[" + Thread.currentThread().getName() + "]更新后,number值為:" + dataDemo.getNumber()); } }
執(zhí)行結(jié)果:
線程[Thread-0] 正在執(zhí)行
線程[Thread-0]更新后,number值為:10
具有可見性驗證通過
結(jié)果分析:
通過對變量number變量添加了volatile關(guān)鍵字修飾,可以看出子線程啟動后將number值改為了10,隨后主線程跳出了while循環(huán),輸出了“具有可見性驗證通過”,說明此時number具有可見性。
原理說明:
通過使用 volatile 關(guān)鍵字,強制從公共內(nèi)存中讀取變量的值,內(nèi)存結(jié)構(gòu)如圖:
到此這篇關(guān)于Java volatile關(guān)鍵字特性講解上篇的文章就介紹到這了,更多相關(guān)Java volatile內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中的轉(zhuǎn)換流InputStreamReader解讀
InputStreamReader是Java.io包中的一個類,用于將字節(jié)輸入流轉(zhuǎn)換為字符輸入流,它繼承自java.io.Reader類,提供了兩種構(gòu)造方法,可以使用默認(rèn)或指定字符集創(chuàng)建實例,常用方法包括讀取字符、判斷是否準(zhǔn)備好讀取數(shù)據(jù)和關(guān)閉流2024-09-09Java中Integer的parseInt和valueOf的區(qū)別詳解
這篇文章主要介紹了Java中Integer的parseInt和valueOf的區(qū)別詳解,nteger.parseInt(s)是把字符串解析成int基本類型,Integer.valueOf(s)是把字符串解析成Integer對象類型,其實int就是Integer解包裝,Integer就是int的包裝,需要的朋友可以參考下2023-11-11SpringBoot?Security使用MySQL實現(xiàn)驗證與權(quán)限管理
安全管理是軟件系統(tǒng)必不可少的的功能。根據(jù)經(jīng)典的“墨菲定律”——凡是可能,總會發(fā)生。如果系統(tǒng)存在安全隱患,最終必然會出現(xiàn)問題,這篇文章主要介紹了SpringBoot安全管理Spring?Security基本配置2022-11-11超詳細(xì)講解SpringCloud?Commons公共抽象的用法
這篇文章主要介紹了超詳細(xì)講解SpringCloud?Commons公共抽象的用法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-04-04文件上傳SpringBoot后端MultipartFile參數(shù)報空問題的解決辦法
這篇文章主要介紹了文件上傳SpringBoot后端MultipartFile參數(shù)報空問題的解決辦法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11springboot結(jié)合mysql主從來實現(xiàn)讀寫分離的方法示例
這篇文章主要介紹了springboot結(jié)合mysql主從來實現(xiàn)讀寫分離的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04