激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語(yǔ)言|JavaScript|易語(yǔ)言|vb.net|

香港云服务器
服務(wù)器之家 - 編程語(yǔ)言 - Java教程 - volatile可見性的一些認(rèn)識(shí)和論證

volatile可見性的一些認(rèn)識(shí)和論證

2020-12-20 13:37陳其苗 Java教程

volatile的關(guān)鍵詞的使用在JVM內(nèi)存模型中已是老生常談了,這篇文章主要結(jié)合自己對(duì)可見性的一些認(rèn)識(shí)和一些直觀的例子來談?wù)剉olatile,感興趣的朋友一起看看吧

一、前言

    volatile的關(guān)鍵詞的使用在JVM內(nèi)存模型中已是老生常談了,這篇文章主要結(jié)合自己對(duì)可見性的一些認(rèn)識(shí)和一些直觀的例子來談?wù)剉olatile。文章正文大致分為三部分,首先會(huì)介紹一下happen-before,接著講解volatile的一些使用場(chǎng)景,最后會(huì)附上一些例子來論證使用與不使用volatile的區(qū)別。

二、happen-before

    對(duì)操作系統(tǒng)有認(rèn)識(shí)的同學(xué)一定知道,CPU一般有三級(jí)緩存,在與內(nèi)存交互的時(shí)候,存在緩存與內(nèi)存的更新問題,其次CPU在讀取指令的時(shí)候,會(huì)做一些指令重排序的工作,提高程序運(yùn)行效率。類比JVM內(nèi)存模型(見下圖),每個(gè)線程擁有自己的工作內(nèi)存,同時(shí)存在一個(gè)主存,線程間通過主存來進(jìn)行通信,同樣的,JVM也存在指令重排序,可見JVM內(nèi)存模型與實(shí)際物理內(nèi)存模型十分相似。(這里順便提一下,編譯器其實(shí)也會(huì)作一定重排序優(yōu)化)。

volatile可見性的一些認(rèn)識(shí)和論證

    作為開發(fā)人員,你不可能了解到每個(gè)JVM優(yōu)化細(xì)節(jié),更不可能了解到CPU何時(shí)會(huì)進(jìn)行指令重排序,所以java語(yǔ)言定義了更上層的一個(gè)概念,就是"happen-before"。起初,我看到這個(gè)單詞的時(shí)候,誤以為這是一個(gè)指令執(zhí)行順序的規(guī)則,后來仔細(xì)想想又發(fā)覺不對(duì)勁。如果”happen-before“僅僅是抽象了指令執(zhí)行順序的概念,那么它就把握不了“工作內(nèi)存將值寫回主存”和“工作內(nèi)存從主存中刷新自己的值”這個(gè)兩個(gè)action的時(shí)機(jī)。那么這個(gè)概念也就變得沒什么意義了。所以!所以!所以!”happen-before“是一個(gè)可見性的原則!!!

下面給出happen-before的具體規(guī)則:

程序次序規(guī)則:一個(gè)線程內(nèi),按照代碼順序,書寫在前面的操作先行發(fā)生于書寫在后面的操作;

鎖定規(guī)則:一個(gè)unLock操作先行發(fā)生于后面對(duì)同一個(gè)鎖額lock操作;

volatile變量規(guī)則:對(duì)一個(gè)變量的寫操作先行發(fā)生于后面對(duì)這個(gè)變量的讀操作;

傳遞規(guī)則:如果操作A先行發(fā)生于操作B,而操作B又先行發(fā)生于操作C,則可以得出操作A先行發(fā)生于操作C;

線程啟動(dòng)規(guī)則:Thread對(duì)象的start()方法先行發(fā)生于此線程的每個(gè)一個(gè)動(dòng)作;

線程中斷規(guī)則:對(duì)線程interrupt()方法的調(diào)用先行發(fā)生于被中斷線程的代碼檢測(cè)到中斷事件的發(fā)生;

線程終結(jié)規(guī)則:線程中所有的操作都先行發(fā)生于線程的終止檢測(cè),我們可以通過Thread.join()方法結(jié)束、Thread.isAlive()的返回值手段檢測(cè)到線程已經(jīng)終止執(zhí)行;

對(duì)象終結(jié)規(guī)則:一個(gè)對(duì)象的初始化完成先行發(fā)生于他的finalize()方法的開始;

三、volatile的使用場(chǎng)景

    happen-before的第三條規(guī)則提到“volatile變量規(guī)則:對(duì)一個(gè)變量的寫操作先行發(fā)生于后面對(duì)這個(gè)變量的讀操作”,也就是說;一個(gè)volatile變量的寫操作對(duì)后續(xù)對(duì)讀操作可見。說白了就是每次寫完volatile變量,都會(huì)將值從工作內(nèi)存寫回到主存中去,每次讀取volatile變量,工作內(nèi)存必須從主存中刷新下自己的值。如此的話,volatile就是為了解決多個(gè)線程共享數(shù)據(jù)的可見性問題。但是不是任何數(shù)據(jù)共享場(chǎng)景都可以使用volatile,必須滿足以下兩種情景才行。

    應(yīng)用場(chǎng)景:

    1.多個(gè)線程不依賴原值的情況下進(jìn)行讀寫操作

    2.一個(gè)線程依賴原值進(jìn)行寫操作,多個(gè)線程進(jìn)行讀操作

在我看來,除了這兩種情況外,無非是多個(gè)線程依賴原值進(jìn)行運(yùn)算,這樣子倒不是說volatile可見性不起作用了,而是無法保證讀取原值和運(yùn)算是一個(gè)原子操作!舉個(gè)簡(jiǎn)單的例子,多個(gè)線程執(zhí)行i++;i是一個(gè)共享變量,由于讀取i的值和i自增不是一個(gè)原子操作,所以i最終會(huì)丟失掉一部分自增過程。代碼如下,最終i輸出的結(jié)果是一個(gè)小于1000的整數(shù)。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
 * Created by chenqimiao on 17/8/23.
 */
public class Testv {
  public static volatile int i = 0;
  public static void main(String args[]){
    for (int i =0;i<1000;i++){
      new Thread(){
        public void run(){
          Thread.yield();
          Testv.i++;
        }
      }.start();
    }
    System.out.println(Testv.i);
  }
}

要滿足以上這種需求,我們還必須賦予代碼原子性,最常用的肯定是鎖操作了,一個(gè)字穩(wěn),性能可觀,同時(shí)保證原子性和可見性。如果想操作一波的話,還可以考慮使用一些無鎖操作,如CAS,象java.util.concurrent包下的一些原子類就是利用了CAS來做到原子性,但原子性并不能保證可見性,這個(gè)時(shí)候,還需要配合volatile。

以上種種都是對(duì)volatile使用場(chǎng)景的概括,想了解具體的使用場(chǎng)景可以參考博文:https://www.ibm.com/developerworks/cn/java/j-jtp06197.html

四、volatile可見性的證明

   先上段代碼好了,不知道從何說起了。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.example.demo.netty;
/**
 * Created with IntelliJ IDEA.
 * User: chenqimiao
 * Date: 2017/8/23
 * Time: 9:16
 * To change this template use File | Settings | File Templates.
 */
public class VolatileTest {
   boolean isStop = false;
  public void test(){
    Thread t1 = new Thread(){
      public void run() {
        isStop=true;
      }
    };
    Thread t2 = new Thread(){
      public void run() {
        while (!isStop);
      }
    };
    t2.start();
    t1.start();
  }
  public static void main(String args[]) throws InterruptedException {
    for (int i =0;i<25;i++){
      new VolatileTest().test();
    }
  }
}

    上面這段代碼可能永遠(yuǎn)也不會(huì)結(jié)束,因?yàn)榫€程一對(duì)isStop的賦值,線程二可能對(duì)此并不可見。當(dāng)然只是可能,所以為了放大可見性問題,我這里作了25次循環(huán)。只要有一組線程,“線程一對(duì)isStop的賦值,線程二對(duì)此不可見”的情況發(fā)生,就不會(huì)退出程序。

    now,假如你給 isStop 添加一個(gè) volatile 關(guān)鍵字,那么你會(huì)發(fā)現(xiàn)程序立馬就會(huì)退出。

總結(jié)

以上所述是小編給大家介紹的volatile可見性的一些認(rèn)識(shí)和論證,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)服務(wù)器之家網(wǎng)站的支持!

原文鏈接:http://www.cnblogs.com/think-in-java/p/7420979.html

延伸 · 閱讀

精彩推薦
600
主站蜘蛛池模板: 2021国产精品 | 国产精品视频专区 | 羞羞视频免费网站 | 中国美女一级黄色大片 | 久久精品亚洲欧美日韩精品中文字幕 | 黄色免费在线视频网站 | 国产精品色综合 | 欧美成人一区免费视频 | 午夜视频你懂的 | 一级大片视频 | 成人福利视频在线观看 | 午夜精品小视频 | 国产一区二区免费看 | 日本精品久久久一区二区三区 | 国产精品视频网 | 欧美成人国产va精品日本一级 | 精品一区二区三区中文字幕老牛 | 免费欧美一级视频 | 亚洲午夜一区二区三区 | 亚州综合图片 | 欧美成人精品欧美一级乱黄 | 亚洲一区国产二区 | 成人综合免费视频 | 精品国产一区二区三区久久久蜜 | 中文字幕激情 | 国产精选久久久 | 欧美综合在线观看视频 | 国产精选电影免费在线观看 | 欧美一区二区黄色 | 成人mm视频在线观看 | 久久婷婷一区二区三区 | 日韩精品久久久久久久九岛 | 青草av.久久免费一区 | 欧美日韩一区三区 | 亚洲视频黄 | 免费观看的毛片手机视频 | 一级黄色免费观看 | 久久久久av69精品 | 亚洲成人久久精品 | 久久久一区二区三区精品 | 国产精品1区2区在线观看 |