午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

volatile 怎么實(shí)現(xiàn)的內(nèi)存可見?沒有它一定不可見嗎?

 新用戶0175WbuX 2022-02-24

  推薦學(xué)習(xí)并發(fā)編程基礎(chǔ)篇:高并發(fā)一瞥,「線程」和「線程池」,細(xì)說一番 真真香!耗時(shí)大半個(gè)月收整全套「Java架構(gòu)進(jìn)階pdf」沒白費(fèi) 一、碼場(chǎng)心得volatile 怎么實(shí)現(xiàn)的內(nèi)存可見?沒有它一定不可見嗎?

  你是個(gè)能吃苦的人嗎?

  從前的能吃苦大多指的體力勞動(dòng)的苦,但現(xiàn)在的能吃苦已經(jīng)包括太多維度,包括:讀書學(xué)習(xí)&寂寞的苦、深度思考&腦力的苦、自律習(xí)慣&修行的苦、自控能力&放棄的苦、低頭做人&尊嚴(yán)的苦。

  雖然這些苦擺在眼前,但大多數(shù)人還是喜歡吃簡(jiǎn)單的苦。熬夜加班、日復(fù)一日、重復(fù)昨天、CRUD,最后身體發(fā)胖、體質(zhì)下降、能力不足、自抱自泣!所以有些苦能不吃就不吃,要吃就吃那些有成長(zhǎng)價(jià)值的苦。

  今天你堅(jiān)持了嗎?

  如果一件小事能堅(jiān)持5年以上,那你一定是很了不起的人。是的,很了不起。人最難的就是想清楚了但做不到,或者偶爾做到長(zhǎng)期做不到。

  其實(shí)大多數(shù)走在研發(fā)路上的伙伴們,都知道自己該努力,但明明下好了的決心就是堅(jiān)持不了多久。直到有一天被瓶頸限制在困局中才會(huì)著急,但這時(shí)候在想破局就真的很難了!

  二、面試題

  謝飛機(jī),小記,飛機(jī)趁著周末,吃完火鍋。又去約面試官喝茶了!

  謝飛機(jī):嗨,我在這,這邊,這邊。

  面試官:你怎么又來了,最近學(xué)的不錯(cuò)了?

  謝飛機(jī):還是想來大廠,別害羞,面我吧!

  面試官:我好像是你補(bǔ)課老師... 既然來了,就問問你吧!volatile 是干啥的?

  謝飛機(jī):啊,volatile 是保證變量對(duì)所有線程的可見性的。

  面試官:那 volatile 可以解決原子性問題嗎?

  謝飛機(jī):不可以!

  面試官:那 volatile 的底層原理是如何實(shí)現(xiàn)的呢?

  謝飛機(jī):...,這!面試官,剛問兩個(gè)題就甩雷,你是不家里有事要忙?

  面試官:你管我!

  三、volatile 講解1. 可見性案例

  public class ApiTest {

  public static void main(String[] args) {

  final VT vt=new VT();

  Thread Thread01=new Thread(vt);

  Thread Thread02=new Thread(new Runnable() {

  public void run() {

  try {

  Thread.sleep(3000);

  } catch (InterruptedException ignore) {

  }

  vt.sign=true;

  System.out.println("vt.sign=true 通知 while (!sign) 結(jié)束!");

  }

  });

  Thread01.start();

  Thread02.start();

  }

  }

  class VT implements Runnable {

  public boolean sign=false;

  public void run() {

  while (!sign) {

  }

  System.out.println("你壞");

  }

  }

  這段代碼,是兩個(gè)線程操作一個(gè)變量,程序期望當(dāng) sign 在線程 Thread01 被操作 vt.sign=true 時(shí),Thread02 輸出 你壞。

  但實(shí)際上這段代碼永遠(yuǎn)不會(huì)輸出 你壞,而是一直處于死循環(huán)。這是為什么呢?接下來我們就一步步講解和驗(yàn)證。

  2. 加上volatile關(guān)鍵字

  我們把 sign 關(guān)鍵字加上 volatitle 描述,如下:

  class VT implements Runnable {

  public volatile boolean sign=false;

  public void run() {

  while (!sign) {

  }

  System.out.println("你壞");

  }

  }

  測(cè)試結(jié)果

  vt.sign=true 通知 while (!sign) 結(jié)束!

  你壞

  Process finished with exit code 0

  volatile關(guān)鍵字是Java虛擬機(jī)提供的的最輕量級(jí)的同步機(jī)制,它作為一個(gè)修飾符出現(xiàn),用來修飾變量,但是這里不包括局部變量哦

  在添加 volatile 關(guān)鍵字后,程序就符合預(yù)期的輸出了 你壞。從我們對(duì) volatile 的學(xué)習(xí)認(rèn)知可以知道。volatile關(guān)鍵字是 JVM 提供的最輕量級(jí)的同步機(jī)制,用來修飾學(xué)歷提升變量,用來保證變量對(duì)所有線程可見性。

  正在修飾后可以讓字段在線程見可見,那么這個(gè)屬性被修改值后,可以及時(shí)的在另外的線程中做出相應(yīng)的反應(yīng)。

  3. volatile怎么保證的可見性3.1 無volatile時(shí),內(nèi)存變化volatile 怎么實(shí)現(xiàn)的內(nèi)存可見?沒有它一定不可見嗎?

  無volatile時(shí),內(nèi)存變化

  首先是當(dāng) sign 沒有 volatitle 修飾時(shí) public boolean sign=false;,線程01對(duì)變量進(jìn)行操作,線程02并不會(huì)拿到變化的值。所以程序也就不會(huì)輸出結(jié)果 “你壞”

  3.2 有volatile時(shí),內(nèi)存變化volatile 怎么實(shí)現(xiàn)的內(nèi)存可見?沒有它一定不可見嗎?

  有volatile時(shí),內(nèi)存變化

  當(dāng)我們把變量使用 volatile 修飾時(shí) public volatile boolean sign=false;,線程01對(duì)變量進(jìn)行操作時(shí),會(huì)把變量變化的值強(qiáng)制刷新的到主內(nèi)存。當(dāng)線程02獲取值時(shí),會(huì)把自己的內(nèi)存里的 sign 值過期掉,之后從主內(nèi)存中讀取。所以添加關(guān)鍵字后程序如預(yù)期輸出結(jié)果。

  4. 反編譯解毒可見性

  類似這樣有深度的技術(shù)知識(shí),最佳的方式就是深入理解原理,看看它到底做了什么才保證的內(nèi)存可見性操作。

  4.1 查看JVM指令

  指令:javap -v -p VT

  public volatile boolean sign;

  descriptor: Z

  flags: ACC_PUBLIC, ACC_VOLATILE

  org.itstack.interview.test.VT();

  descriptor: ()V

  flags:

  Code:

  stack=2, locals=1, args_size=1

  0: aload_0

  1: invokespecial #1 // Method java/lang/Object."":()V

  4: aload_0

  5: iconst_0

  6: putfield #2 // Field sign:Z

  9: return

  LineNumberTable:

  line 35: 0

  line 37: 4

  LocalVariableTable:

  Start Length Slot Name Signature

  0 10 0 this Lorg/itstack/interview/test/VT;

  public void run();

  descriptor: ()V

  flags: ACC_PUBLIC

  Code:

  stack=2, locals=1, args_size=1

  0: aload_0

  1: getfield #2 // Field sign:Z

  4: ifne 10

  7: goto 0

  10: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;

  13: ldc #4 // String 你壞

  15: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V

  18: return

  LineNumberTable:

  line 40: 0

  line 42: 10

  line 43: 18

  LocalVariableTable:

  Start Length Slot Name Signature

  0 19 0 this Lorg/itstack/interview/test/VT;

  StackMapTable: number_of_entries = 2

  frame_type = 0 /* same */

  frame_type = 9 /* same */

  }

  從JVM指令碼中只會(huì)發(fā)現(xiàn)多了,ACC_VOLATILE,并沒有什么其他的點(diǎn)。所以,也不能看出是怎么實(shí)現(xiàn)的可見性。

  4.2 查看匯編指令

  通過Class文件查看匯編,需要下載 hsdis-amd64.dll 文件,復(fù)制到 JAVA_HOME\jre\bin\server目錄下。下載資源如下:

  vorboss.dl.sourceforge/project/fcml/fcml-1.1.1/hsdis-1.1.1-win32-amd64.zipvorboss.dl.sourceforge/project/fcml/fcml-1.1.1/hsdis-1.1.1-win32-i386.zip

  另外是執(zhí)行命令,包括:基礎(chǔ)指令:java -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly指定打?。?XX:CompileCommand=dontinline,類名.方法名指定打?。?XX:CompileCommand=compileonly,類名.方法名輸出位置:> xxx

  最終使用:java -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=dontinline,ApiTest.main -XX:CompileCommand=compileonly,ApiTest.mian

  指令可以在IDEA中的 Terminal 里使用,也可以到 DOS黑窗口中使用

  另外,為了更簡(jiǎn)單的使用,我們把指令可以配置到idea的 VM options 里,如下圖:

  volatile 怎么實(shí)現(xiàn)的內(nèi)存可見?沒有它一定不可見嗎?

  Idea VM options 配置編譯指令

  配置完成后,不出意外的運(yùn)行結(jié)果如下:

  Loaded disassembler from C:\Program Files\Java\jdk1.8.0_161\jre\bin\server\hsdis-amd64.dll

  Decoding compiled method 0x0000000003744990:

  Code:

  Argument 0 is unknown.RIP: 0x3744ae0 Code size: 0x00000110

  [Disassembling for mach='amd64']

  [Entry Point]

  [Constants]

  # {method} {0x000000001c853d18} 'getSnapshotTransformerList' '()[Lsun/instrument/TransformerManager$TransformerInfo;' in 'sun/instrument/TransformerManager'

  # [sp+0x40] (sp of caller)

  0x0000000003744ae0: mov r10d,dword ptr [rdx+8h]

  0x0000000003744ae4: shl r10,3h

  0x0000000003744ae8: cmp r10,rax

  0x0000000003744aeb: jne 3685f60h ; {runtime_call}

  0x0000000003744af1: nop word ptr [rax+rax+0h]

  0x0000000003744afc: nop

  [Verified Entry Point]

  0x0000000003744b00: mov dword ptr [rsp+0ffffffffffffa000h],eax

  0x0000000003744b07: push rbp

  0x0000000003744b08: sub rsp,30h ;*aload_0

  ; - sun.instrument.TransformerManager::getSnapshotTransformerList@0 (line 166)

  0x0000000003744b0c: mov eax,dword ptr [rdx+10h]

  0x0000000003744b0f: shl rax,3h ;*getfield mTransformerList

  ; - sun.instrument.TransformerManager::getSnapshotTransformerList@1 (line 166)

  0x0000000003744b13: add rsp,30h

  ...

  運(yùn)行結(jié)果就是匯編指令,比較多這里就不都放了。我們只觀察重點(diǎn)部分:

  0x0000000003324cda: mov 0x74(%r8),%edx ;*getstatic state

  ; - VT::run@28 (line 27)

  0x0000000003324cde: inc %edx

  0x0000000003324ce0: mov %edx,0x74(%r8)

  0x0000000003324ce4: lock addl $0x0,(%rsp) ;*putstatic state

  ; - VT::run@33 (line 27)

  編譯后的匯編指令中,有volatile關(guān)鍵字和沒有volatile關(guān)鍵字,主要差別在于多了一個(gè) lock addl $0x0,(%rsp),也就是lock的前綴指令。

  lock指令相當(dāng)于一個(gè)內(nèi)存屏障,它保證如下三點(diǎn):

  將本處理器的緩存寫入內(nèi)存。重排序時(shí)不能把后面的指令重排序到內(nèi)存屏障之前的位置。如果是寫入動(dòng)作會(huì)導(dǎo)致其他處理器中對(duì)應(yīng)的內(nèi)存無效。

  那么,這里的1、3就是用來保證被修飾的變量,保證內(nèi)存可見性。

  5. 不加volatile也可見嗎

  有質(zhì)疑就要有驗(yàn)證

  我們現(xiàn)在再把例子修改下,在 while (!sign) 循環(huán)體中添加一段執(zhí)行代碼,如下;

  class VT implements Runnable {

  public boolean sign=false;

  public void run() {

  while (!sign) {

  System.out.println("你好");

  }

  System.out.println("你壞");

  }

  }

  修改后去掉了 volatile 關(guān)鍵字,并在while循環(huán)中添加一段代碼?,F(xiàn)在的運(yùn)行結(jié)果是:

  ...

  你好

  你好

  你好

  vt.sign=true 通知 while (!sign) 結(jié)束!

  你壞

  Process finished with exit code 0

  咋樣,又可見了吧!

  這是因?yàn)樵跊] volatile 修飾時(shí),jvm也會(huì)盡量保證可見性。有 volatile 修飾的時(shí)候,一定保證可見性。

  四、總結(jié)最后我們?cè)倏偨Y(jié)下 volatile,它呢,會(huì)控制被修飾的變量在內(nèi)存操作上主動(dòng)把值刷新到主內(nèi)存,JMM 會(huì)把該線程對(duì)應(yīng)的CPU內(nèi)存設(shè)置過期,從主內(nèi)存中讀取最新值。那么,volatile 如何防止指令重排也是內(nèi)存屏障,volatile 的內(nèi)存屏障是(寫操作前插入StoreStore、寫操作后添加StoreLoad、讀操作前添加LoadLoad、讀操作后添加LoadStore),也就是四個(gè)位置,來保證重排序時(shí)不能把內(nèi)存屏障后面的指令重排序到內(nèi)存屏障之前的位置。另外 volatile 并不能解決原子性,如果需要解決原子性問題,需要使用 synchronzied 或者 lock。

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多