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

Java中Volatile關(guān)鍵字能保證原子性嗎

 更新時(shí)間:2022年08月28日 15:27:03   作者:碼農(nóng)小宋???????  
這篇文章主要介紹了Java中Volatile關(guān)鍵字能保證原子性嗎,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下

volatile

volatile 是 Java 中的一個(gè)相對(duì)來說比較重要的關(guān)鍵字,主要就是用來修飾會(huì)被不同線程訪問和修改的變量。

而這個(gè)變量只能保證兩個(gè)特性,一個(gè)是保證有序性,另外一個(gè)則是保證可見性。

那么什么是有序性,什么又是可見性呢?

有序性

那么什么是有序性呢?

其實(shí)程序執(zhí)行的順序按照代碼的先后順序執(zhí)行,禁止進(jìn)行指令重排序。

看似理所當(dāng)然,其實(shí)并不是這樣,指令重排序是JVM為了優(yōu)化指令,提高程序運(yùn)行效率,在不影響單線程程序執(zhí)行結(jié)果的前提下,盡可能地提高并行度。

但是在??多線程??環(huán)境下,有些代碼的順序改變,有可能引發(fā)邏輯上的不正確。

而 volatile 就是因?yàn)橛羞@個(gè)特性,所以才被大家熟知的。

volatile 又是如何保證有序性的呢?

有很多小伙伴就說,網(wǎng)上說的是 volatile 可以禁止指令指令重排序,這就保證了代碼的程序會(huì)嚴(yán)格按照代碼的先后順序執(zhí)行。這就保證了有序性。被 volatile 修飾的變量的操作,會(huì)嚴(yán)格按照代碼順序執(zhí)行,就是說當(dāng)代碼執(zhí)行到 volatile 修飾的變量時(shí),其前面的代碼一定執(zhí)行完畢,后面的代碼一定沒有執(zhí)行。

如果這時(shí)候,面試官不再繼續(xù)深挖下去的話,那么恭喜你,可能這個(gè)問題已經(jīng)回答完了,但是如果面試官繼續(xù)往下深挖,為什么會(huì)禁止指令重排,什么又是指令重排呢?

在從源碼到指令的執(zhí)行,一般是分成了三種重排,如圖所示:                             

我們接下來就得看看 volatile 是如何禁止指令重排的。

我們直接用代碼來進(jìn)行驗(yàn)證:

public class ReSortDemo {

int a = 0;
boolean flag = false;

public void mehtod1(){
a = 1;
flag = true;
}

public void method2(){
if(flag){
a = a +1;
System.out.println("最后的值: "+a);
}
}
}

如果有人看到這段代碼,肯定會(huì)說,那這段代碼出來的結(jié)果會(huì)是什么呢?

有些人說是 2,是的, 如果你只是單線程調(diào)用,那結(jié)果就是 2,但是如果是多線程調(diào)用的時(shí)候,最后的輸出結(jié)果不一定是我們想象到的 2,這時(shí)就要把兩個(gè)變量都設(shè)置為 volatile。

如果大家對(duì)單例模式了解比較多的話,肯定也是關(guān)注過這個(gè) volatile,為什么呢?

大家看看如下代碼:

class Singleton {
// 不是一個(gè)原子性操作
//private static Singleton instance;
//改進(jìn),Volatile 可以保持可見性,不能保證原子性,由于內(nèi)存屏障,可以保證避免指令重排的現(xiàn)象產(chǎn)生!
private static volatile Singleton instance;

// 構(gòu)造器私有化
private Singleton() {
}

// 提供一個(gè)靜態(tài)的公有方法,加入雙重檢查代碼,解決線程安全問題, 同時(shí)解決懶加載問題,同時(shí)保證了效率, 推薦使用
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}

上面的單例模式大家熟悉么?

是的,這就是 **雙重檢查(DCL 懶漢式) **

有人會(huì)說,因?yàn)橛兄噶钪嘏判虻拇嬖?,雙端檢索機(jī)制也也不一定是線程安全的呀,對(duì)呀,所以用到了 synchronized 關(guān)鍵字,讓他變成了線程安全的了。

可見性

其實(shí)可見性就是,在多線程環(huán)境中,對(duì)共享變量的修改對(duì)于其他線程是否立即可見的問題。

那么他的可見性一般都會(huì)表現(xiàn)在什么地方呢?用在什么地方呢?

其實(shí)一般用這個(gè)變量,很多都是為了保證他的可見性,就比如定義的一個(gè)全局變量,在其中有個(gè)循環(huán)來判斷這個(gè)變量的值,有一個(gè)線程修改了這個(gè)參數(shù)的時(shí)候,這個(gè)循環(huán)會(huì)停止,跳轉(zhuǎn)到之后去執(zhí)行。

我們來看看沒有使用volatile修飾代碼實(shí)現(xiàn):

public class Test {

private static boolean flag = false;

public static void main(String[] args) throws Exception{
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("線程A開始執(zhí)行:");
for (;;){
if (flag){
System.out.println("跳出循環(huán)");
break;
}
}
}
}).start();
Thread.sleep(100);

new Thread(new Runnable() {
@Override
public void run() {
System.out.println("線程B開始執(zhí)行");
flag = true;
System.out.println("標(biāo)識(shí)已經(jīng)變更");
}
}).start();
}
}

結(jié)果大家肯定是可想而知,

運(yùn)行結(jié)果肯定是:

線程A開始執(zhí)行:
線程B開始執(zhí)行
標(biāo)識(shí)已經(jīng)變更

確實(shí),就是這樣的。

如果我們用 volatile 呢,那么這個(gè)代碼的執(zhí)行結(jié)果就會(huì)不一樣呢?

我們來試一下:

public class Test {

private static volatile boolean flag = false;

public static void main(String[] args) throws Exception{
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("線程A開始執(zhí)行:");
for (;;){
if (flag){
System.out.println("跳出循環(huán)");
break;
}
}
}
}).start();
Thread.sleep(100);

new Thread(new Runnable() {
@Override
public void run() {
System.out.println("線程B開始執(zhí)行");
flag = true;
System.out.println("標(biāo)識(shí)已經(jīng)變更");
}
}).start();
}

這樣我們就能看到另外一個(gè)執(zhí)行結(jié)果,在循環(huán)當(dāng)中的輸出語句是可以被執(zhí)行的。

也就是說,在線程B 中,我們?nèi)バ薷倪@個(gè)被修飾的變量,那么最終,在線程A中,就能順利讀取到我們的數(shù)據(jù)信息了。

是否能夠保證原子性

不能,我們來看一點(diǎn)代碼,被volatile修飾的變量,

public class Test {

// volatile不保證原子性
// 原子性:保證數(shù)據(jù)一致性、完整性
volatile int number = 0;

public void addPlusPlus() {
number++;
}

public static void main(String[] args) {
Test volatileAtomDemo = new Test();
for (int j = 0; j < 20; j++) {
new Thread(() -> {
for (int i = 0; i < 1000; i++) {
volatileAtomDemo.addPlusPlus();
}
}, String.valueOf(j)).start();
}// 后臺(tái)默認(rèn)兩個(gè)線程:一個(gè)是main線程,一個(gè)是gc線程
while (Thread.activeCount() > 2) {
Thread.yield();
}
// 如果volatile保證原子性的話,最終的結(jié)果應(yīng)該是20000 // 但是每次程序執(zhí)行結(jié)果都不等于20000
System.out.println(Thread.currentThread().getName() +
" final number result = " + volatileAtomDemo.number);
}
}

如果能夠保原子性,那么最終的結(jié)果應(yīng)該是20000,但是每次的最終結(jié)果并不能保證就是20000,比如:

main final number result = 17114
main final number result = 20000
main final number result = 19317

三次執(zhí)行,都是不同的結(jié)果,

為什么會(huì)出現(xiàn)這種呢?這就和number++有關(guān)系了

number++被拆分成3個(gè)指令

  • 執(zhí)行GETFIELD拿到主內(nèi)存中的原始值number
  • 執(zhí)行IADD進(jìn)行加1操作
  • 執(zhí)行PUTFIELD把工作內(nèi)存中的值寫回主內(nèi)存中

當(dāng)多個(gè)線程并發(fā)執(zhí)行PUTFIELD指令的時(shí)候,會(huì)出現(xiàn)寫回主內(nèi)存覆蓋問題,所以才會(huì)導(dǎo)致最終結(jié)果不為 20000,所以 volatile 不能保證原子性。

到此這篇關(guān)于Java中Volatile關(guān)鍵字能保證原子性嗎的文章就介紹到這了,更多相關(guān)Java Volatile關(guān)鍵內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java+Swing實(shí)現(xiàn)五子棋游戲的示例代碼

    Java+Swing實(shí)現(xiàn)五子棋游戲的示例代碼

    本文將通過Java語言實(shí)現(xiàn)經(jīng)典游戲—五子棋游戲,文中采用了Swing制作游戲界面,具有開始游戲,悔棋,認(rèn)輸,退出等功能。感興趣的可以跟隨小編一起動(dòng)手試一試
    2022-02-02
  • 史上最通俗理解的Java死鎖代碼演示

    史上最通俗理解的Java死鎖代碼演示

    這篇文章主要給大家介紹了關(guān)于Java死鎖代碼演示的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java高并發(fā)中的交換器Exchanger解析

    Java高并發(fā)中的交換器Exchanger解析

    這篇文章主要介紹了Java高并發(fā)中的交換器Exchanger解析,如果兩個(gè)線程并行處理,但在某個(gè)時(shí)刻需要互相交換自己已經(jīng)處理完的中間數(shù)據(jù),然后才能繼續(xù)往下執(zhí)行,這個(gè)時(shí)候就可以使用 Exchanger,需要的朋友可以參考下
    2023-12-12
  • 如何使用JAVA實(shí)現(xiàn)數(shù)字水印

    如何使用JAVA實(shí)現(xiàn)數(shù)字水印

    本文介紹了如何使用JAVA實(shí)現(xiàn)數(shù)字水印,主要用到了java.awt包中的AlphaComposite類,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2015-07-07
  • Mybatis-Plus如何使用分頁實(shí)例詳解

    Mybatis-Plus如何使用分頁實(shí)例詳解

    最近在研究mybatis,然后就去找簡(jiǎn)化mybatis開發(fā)的工具,下面這篇文章主要給大家介紹了關(guān)于Mybatis-Plus如何使用分頁的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-03-03
  • 如何測(cè)試Java類的線程安全性

    如何測(cè)試Java類的線程安全性

    這篇文章主要介紹了如何測(cè)試Java類的線程安全性,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • Java開發(fā)環(huán)境jdk 1.8安裝配置方法(Win7 64位系統(tǒng)/windows server 2008)

    Java開發(fā)環(huán)境jdk 1.8安裝配置方法(Win7 64位系統(tǒng)/windows server 2008)

    這篇文章主要介紹了Java開發(fā)環(huán)境配置方法(Win7 64位系統(tǒng)/windows server 2008),需要的朋友可以參考下
    2016-10-10
  • Java實(shí)現(xiàn)開箱即用的redis分布式鎖

    Java實(shí)現(xiàn)開箱即用的redis分布式鎖

    這篇文章主要為大家詳細(xì)介紹了如何使用Java實(shí)現(xiàn)開箱即用的基于redis的分布式鎖,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以收藏一下
    2022-12-12
  • 詳解MyBatis-Plus updateById方法更新不了空字符串/null解決方法

    詳解MyBatis-Plus updateById方法更新不了空字符串/null解決方法

    這篇文章主要介紹了詳解MyBatis-Plus updateById方法更新不了空字符串/null解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java如何對(duì)方法進(jìn)行調(diào)用詳解

    Java如何對(duì)方法進(jìn)行調(diào)用詳解

    今天給大家整理了Java如何對(duì)方法進(jìn)行調(diào)用,文中有非常詳細(xì)的介紹及代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們很有幫助,需要的朋友可以參考下
    2021-06-06

最新評(píng)論

正安县| 韶关市| 阿克陶县| 长泰县| 闽侯县| 荥阳市| 策勒县| 义马市| 乳山市| 黄大仙区| 惠州市| 曲麻莱县| 高邮市| 东丰县| 枣强县| 会昌县| 常宁市| 九龙县| 海阳市| 锡林浩特市| 灵宝市| 甘南县| 徐州市| 绥化市| 噶尔县| 黄龙县| 定边县| 佛坪县| 南召县| 财经| 博乐市| 长子县| 华蓥市| 永安市| 石嘴山市| 谷城县| 巩义市| 镇巴县| 崇仁县| 金坛市| 竹溪县|