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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

volatile变量与普通变量的区别

發(fā)布時(shí)間:2025/4/5 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 volatile变量与普通变量的区别 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

我們通常會(huì)用volatile實(shí)現(xiàn)一些需要線程安全的代碼(也有很多人不敢用,因?yàn)椴涣私?#xff09;,但事實(shí)上volatile本身并不是線程安全的,相對(duì)于synchoronized,它有更多的使用局限性,只能限制在某些特定的場(chǎng)景。本篇文章的目的就是讓大家對(duì) volatile 在本質(zhì)上有個(gè)把握,為了達(dá)到這個(gè)目的,我們會(huì)從java 的內(nèi)存模型及變量操作的內(nèi)存管理來(lái)說明(不用怕,你會(huì)發(fā)現(xiàn)很簡(jiǎn)單)。

一、內(nèi)存模型

可以將內(nèi)存簡(jiǎn)單分為兩種:工作內(nèi)存和主內(nèi)存。所有的數(shù)據(jù)最終都需要存儲(chǔ)在主內(nèi)存,工作內(nèi)存是線程獨(dú)有的,線程之間無(wú)任何干擾。java的內(nèi)存模型主要就是定義工作內(nèi)存和主內(nèi)存的交互,即工作內(nèi)存如何從主內(nèi)存拷貝數(shù)據(jù),以入如何寫數(shù)據(jù)。java 定義了8種原子性操作來(lái)完成工作內(nèi)存與主內(nèi)存的交互:

  • lock 將對(duì)象變成線程獨(dú)占的狀態(tài)
  • unlock 將線程獨(dú)占狀態(tài)的對(duì)象的鎖釋放出來(lái)
  • read 從主內(nèi)存讀數(shù)據(jù)
  • load 將從主內(nèi)存讀取的數(shù)據(jù)寫入工作內(nèi)存
  • use 工作內(nèi)存使用對(duì)象
  • assign 對(duì)工作內(nèi)存中的對(duì)象進(jìn)行賦值
  • store 將工作內(nèi)存中的對(duì)象傳送到主內(nèi)存當(dāng)中
  • write 將對(duì)象寫入主內(nèi)存當(dāng)中,并覆蓋舊值

這些操作也是有一定的條件限制的:
read 和load,store和write 必須成對(duì)出現(xiàn),即從主內(nèi)存中讀數(shù)據(jù)的數(shù)據(jù)工作內(nèi)存必須接受;傳遞到主內(nèi)存的數(shù)據(jù),也不可以被拒絕寫入。
assign后的對(duì)象必須回寫到緩存
未進(jìn)行新賦值的對(duì)象不允許回寫到主內(nèi)存
新的變量只能在主內(nèi)存產(chǎn)生,且未完成初始化的對(duì)象不允許在工作內(nèi)存中使用
對(duì)象只允許被一條線程鎖定,且可以被此線程多次鎖定
未被鎖定的對(duì)象不允許執(zhí)行unlock操作
對(duì)一個(gè)對(duì)象執(zhí)行unlock之前,必須將對(duì)象回寫到主內(nèi)存
java的8種原子性操作,相互之前有一定的約束條件,但并沒有嚴(yán)格限制任意兩個(gè)操作必須連續(xù)出現(xiàn),只是表示成對(duì)出現(xiàn),這也是為什么會(huì)產(chǎn)生線程不安全性的原因。
介紹了上述的背景知識(shí),那我們就來(lái)看一下volatile變量到底和普通變量有啥差別吧

二、volatile變量與普通變量

2.1 volatile 的安全性

下面我們用一個(gè)例子來(lái)說明volatile變量與普通變量的區(qū)別。
假設(shè)有兩個(gè)線程操作一個(gè)主內(nèi)存的對(duì)象,且線程1早于線程2開始(如下例如示一個(gè)a++操作))

public class ThreadSafeTest {public static int a = 0;public static void increase() {a++;}public static void main (String[] args) {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {for (int j = 0; j < 100; j++) {increase();}}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {for (int j = 0; j < 100; j++) {increase();}}});t1.start();t2.start();} }復(fù)制代碼

線程2讀取主內(nèi)存對(duì)象(a)時(shí),可能發(fā)生在幾個(gè)時(shí)期:read之前、read之后、load之后、use之后、assign之后、 store之后、write之后(如下圖所示);

假設(shè)線程1執(zhí)行了a++,a從0變成了1,還未來(lái)得及寫回主內(nèi)存對(duì)象,線程2從主內(nèi)存對(duì)象中讀取的數(shù)據(jù)a=0;此時(shí)線程1寫入主內(nèi)存a=1,而線程2仍然執(zhí)行完了a++ ,此時(shí)仍然 等于1(應(yīng)該等于2),實(shí)際上,這就上相當(dāng)于線程2 讀入了一個(gè)過期的數(shù)據(jù),導(dǎo)致線程不安全。

那如果將a變成volatile對(duì)象是否就正確了呢?
volatile對(duì)對(duì)象的操作做了更嚴(yán)格的限制:

  • use之前不進(jìn)行read和load
  • assign之后必須緊跟store和write
    實(shí)際相當(dāng)于將read load use 三個(gè)原子操作變成一個(gè)原子操作;將assign-store-write變成一個(gè)原子操作。很多文章上都講volatile對(duì)所有的線程是可見的,指的就是執(zhí)行完了assign之后立即就會(huì)回寫主內(nèi)存;在任意一個(gè)線程讀取主內(nèi)存對(duì)象時(shí),都會(huì)刷新主內(nèi)存。在主內(nèi)存中表現(xiàn)是數(shù)據(jù)一致性的,但是各線程內(nèi)存當(dāng)中卻不一定是一致性的。
    同樣是上面的代碼,換成volatile
    ```
    public class ThreadSafeTest {
    public static volatile int a = 0;

    public static void increase() {

    a++;復(fù)制代碼

    }

public static void main (String[] args) {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {for (int j = 0; j < 100; j++) {increase();}}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {for (int j = 0; j < 100; j++) {increase();}}});t1.start();t2.start();}復(fù)制代碼

}

```
運(yùn)行后發(fā)現(xiàn),也拿不到正確的結(jié)果(如果拿到請(qǐng)把j的數(shù)值調(diào)大)。操你媽,不是說是線程安全的變量嗎?為啥也不正確?
這是因?yàn)榫€程內(nèi)部的數(shù)據(jù)仍然有可能存在不一致性,比如,如果線程2讀取數(shù)據(jù)時(shí),處在線程1use之后,但線程1此時(shí)還未來(lái)得及回寫主緩存,這時(shí)候線程2使用到的數(shù)據(jù)仍然是0,兩個(gè)線程同時(shí)對(duì)0++,得到的結(jié)果只會(huì)是1,而不是理想中的2。

2.2 volatile 的線程安全是有條件的

即然volatile 是非線程安全的,那要它還有什么用呢?如果你看過我寫過的“線程安全”的文章應(yīng)該知道,所有的對(duì)象都是相對(duì)線程安全的,也就是有條件的。volatile的線程安全當(dāng)然也是有條件的,它是對(duì)synchronized這一重量級(jí)線程同步的一種補(bǔ)充,其整體性能上優(yōu)于synchronized。那volatile的線程安全的條件是什么呢?適合使用在哪些場(chǎng)景?
《java虛擬機(jī)》給出兩個(gè)條件:

  • 運(yùn)算結(jié)果并不依賴變量的當(dāng)前值(即結(jié)果對(duì)產(chǎn)生中間結(jié)果不依賴),或者能夠確保只有單一的線程修改變量的值
  • 變量不需要與其它的狀態(tài)變量共同參與不變約束(我認(rèn)為此條多此一舉,這個(gè)其它變量也必須得是線程安全的才行)

那適合哪些場(chǎng)景呢?這個(gè)我就不一一舉例了,一個(gè)哥門總結(jié)得很好,參考如下:www.ibm.com/developerwo…

總結(jié)

以上是生活随笔為你收集整理的volatile变量与普通变量的区别的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

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