日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

认识Java中volatile关键字

發(fā)布時(shí)間:2024/10/8 java 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 认识Java中volatile关键字 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

概念

1.可見性:

? ? ? ?可見性是一種復(fù)雜的屬性,因?yàn)榭梢娦灾械腻e(cuò)誤總是會(huì)違背我們的直覺。通常,我們無法確保執(zhí)行讀操作的線程能適時(shí)地看到其他線程寫入的值,有時(shí)甚至是根本不可能的事情。為了確保多個(gè)線程之間對(duì)內(nèi)存寫入操作的可見性,必須使用同步機(jī)制。

  可見性,是指線程之間的可見性,一個(gè)線程修改的狀態(tài)對(duì)另一個(gè)線程是可見的。也就是一個(gè)線程修改的結(jié)果。另一個(gè)線程馬上就能看到。比如:用volatile修飾的變量,就會(huì)具有可見性。volatile修飾的變量不允許線程內(nèi)部緩存和重排序,即直接修改內(nèi)存。所以對(duì)其他線程是可見的。但是這里需要注意一個(gè)問題,volatile只能讓被他修飾內(nèi)容具有可見性,但不能保證它具有原子性。比如 volatile int a = 0;之后有一個(gè)操作 a++;這個(gè)變量a具有可見性,但是a++ 依然是一個(gè)非原子操作,也就是這個(gè)操作同樣存在線程安全問題。

  在 Java 中 volatile、synchronized 和 final 實(shí)現(xiàn)可見性。

2.原子性:

  原子是世界上的最小單位,具有不可分割性。比如 a=0;(a非long和double類型) 這個(gè)操作是不可分割的,那么我們說這個(gè)操作時(shí)原子操作。再比如:a++; 這個(gè)操作實(shí)際是a = a + 1;是可分割的,所以他不是一個(gè)原子操作。非原子操作都會(huì)存在線程安全問題,需要我們使用同步技術(shù)(sychronized)來讓它變成一個(gè)原子操作。一個(gè)操作是原子操作,那么我們稱它具有原子性。java的concurrent包下提供了一些原子類,我們可以通過閱讀API來了解這些原子類的用法。比如:AtomicInteger、AtomicLong、AtomicReference等。

  在 Java 中 synchronized 和在 lock、unlock 中操作保證原子性。

?

3.有序性:

  Java 語言提供了 volatile 和 synchronized 兩個(gè)關(guān)鍵字來保證線程之間操作的有序性,volatile 是因?yàn)槠浔旧戆敖怪噶钪嘏判颉钡恼Z義,synchronized 是由“一個(gè)變量在同一個(gè)時(shí)刻只允許一條線程對(duì)其進(jìn)行 lock 操作”這條規(guī)則獲得的,此規(guī)則決定了持有同一個(gè)對(duì)象鎖的兩個(gè)同步塊只能串行執(zhí)行。

?

下面內(nèi)容摘錄自《Java Concurrency in Practice》:

  下面一段代碼在多線程環(huán)境下,將存在問題。

+ View code /*** @author zhengbinMac*/ public class NoVisibility {private static boolean ready;private static int number;private static class ReaderThread extends Thread {@Overridepublic void run() {while(!ready) {Thread.yield();}System.out.println(number);}}public static void main(String[] args) {new ReaderThread().start();number = 42;ready = true;} }

?

? ? ? ?NoVisibility可能會(huì)持續(xù)循環(huán)下去,因?yàn)樽x線程可能永遠(yuǎn)都看不到ready的值。甚至NoVisibility可能會(huì)輸出0,因?yàn)樽x線程可能看到了寫入ready的值,但卻沒有看到之后寫入number的值,這種現(xiàn)象被稱為“重排序”。只要在某個(gè)線程中無法檢測(cè)到重排序情況(即使在其他線程中可以明顯地看到該線程中的重排序),那么就無法確保線程中的操作將按照程序中指定的順序來執(zhí)行。當(dāng)主線程首先寫入number,然后在沒有同步的情況下寫入ready,那么讀線程看到的順序可能與寫入的順序完全相反。

  在沒有同步的情況下,編譯器、處理器以及運(yùn)行時(shí)等都可能對(duì)操作的執(zhí)行順序進(jìn)行一些意想不到的調(diào)整。在缺乏足夠同步的多線程程序中,要想對(duì)內(nèi)存操作的執(zhí)行春旭進(jìn)行判斷,無法得到正確的結(jié)論。

  這個(gè)看上去像是一個(gè)失敗的設(shè)計(jì),但卻能使JVM充分地利用現(xiàn)代多核處理器的強(qiáng)大性能。例如,在缺少同步的情況下,Java內(nèi)存模型允許編譯器對(duì)操作順序進(jìn)行重排序,并將數(shù)值緩存在寄存器中。此外,它還允許CPU對(duì)操作順序進(jìn)行重排序,并將數(shù)值緩存在處理器特定的緩存中。

?

?

Volatile原理

Java語言提供了一種稍弱的同步機(jī)制,即volatile變量,用來確保將變量的更新操作通知到其他線程。當(dāng)把變量聲明為volatile類型后,編譯器與運(yùn)行時(shí)都會(huì)注意到這個(gè)變量是共享的,因此不會(huì)將該變量上的操作與其他內(nèi)存操作一起重排序。volatile變量不會(huì)被緩存在寄存器或者對(duì)其他處理器不可見的地方,因此在讀取volatile類型的變量時(shí)總會(huì)返回最新寫入的值。

  在訪問volatile變量時(shí)不會(huì)執(zhí)行加鎖操作,因此也就不會(huì)使執(zhí)行線程阻塞,因此volatile變量是一種比sychronized關(guān)鍵字更輕量級(jí)的同步機(jī)制。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

  當(dāng)對(duì)非 volatile 變量進(jìn)行讀寫的時(shí)候,每個(gè)線程先從內(nèi)存拷貝變量到CPU緩存中。如果計(jì)算機(jī)有多個(gè)CPU,每個(gè)線程可能在不同的CPU上被處理,這意味著每個(gè)線程可以拷貝到不同的 CPU cache 中。

  而聲明變量是 volatile 的,JVM 保證了每次讀變量都從內(nèi)存中讀,跳過 CPU cache 這一步。

1.當(dāng)一個(gè)變量定義為 volatile 之后,將具備兩種特性:

  1.保證此變量對(duì)所有的線程的可見性,這里的“可見性”,如本文開頭所述,當(dāng)一個(gè)線程修改了這個(gè)變量的值,volatile 保證了新值能立即同步到主內(nèi)存,以及每次使用前立即從主內(nèi)存刷新。但普通變量做不到這點(diǎn),普通變量的值在線程間傳遞均需要通過主內(nèi)存來完成。

  2.禁止指令重排序優(yōu)化。有volatile修飾的變量,賦值后多執(zhí)行了一個(gè)“l(fā)oad addl $0x0, (%esp)”操作,這個(gè)操作相當(dāng)于一個(gè)內(nèi)存屏障(指令重排序時(shí)不能把后面的指令重排序到內(nèi)存屏障之前的位置),只有一個(gè)CPU訪問內(nèi)存時(shí),并不需要內(nèi)存屏障;(什么是指令重排序:是指CPU采用了允許將多條指令不按程序規(guī)定的順序分開發(fā)送給各相應(yīng)電路單元處理)。

2.volatile 性能:

  volatile 的讀性能消耗與普通變量幾乎相同,但是寫操作稍慢,因?yàn)樗枰诒镜卮a中插入許多內(nèi)存屏障指令來保證處理器不發(fā)生亂序執(zhí)行。

總結(jié)

以上是生活随笔為你收集整理的认识Java中volatile关键字的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。