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

歡迎訪問(wèn) 生活随笔!

生活随笔

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

编程问答

java i 线程不安全_java中的++i是线程安全的吗?

發(fā)布時(shí)間:2024/1/23 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java i 线程不安全_java中的++i是线程安全的吗? 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

java中的++i是線程安全的嗎?為什么?怎么使它線程安全呢?

先說(shuō)答案:

非線程安全

先說(shuō)下為什么是非線程安全的?

從Java內(nèi)存模型說(shuō)起

Java內(nèi)存模型規(guī)定了所有的便利都存儲(chǔ)在主內(nèi)存中,每個(gè)線程還有自己的工作內(nèi)存,工作內(nèi)存中保存了該線程使用到的變量的主內(nèi)存副本拷貝,線程對(duì)變量的所有操作都必須在工作內(nèi)存中進(jìn)行,不能直接讀寫(xiě)主內(nèi)存中的變量。不同的線程之間的也無(wú)法直接訪問(wèn)對(duì)方的工作內(nèi)存中的變量。

下圖是線程、主內(nèi)存、工作內(nèi)存三者的交互關(guān)系??(考慮到j(luò)ava內(nèi)存模型跟物理機(jī)類(lèi)似,故放在這里作對(duì)比)

??

另Java內(nèi)存模型定義了8中操作來(lái)表述主內(nèi)存與工作內(nèi)存之間具體的交互協(xié)議

lock(鎖定):作用于主內(nèi)存的變量,它把主內(nèi)存的某一個(gè)變量標(biāo)示為一個(gè)線程獨(dú)占

unlock(解鎖):作用于主內(nèi)存的變量,變量從鎖定的狀態(tài)釋放出來(lái),釋放后的變量可以被其他線程鎖定

read(讀取):作用于主內(nèi)存的變量,它把一個(gè)變量的值從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存中,以便隨后的load命令動(dòng)作使用。

load(載入):作用于工作內(nèi)存的變量,它把read操作從主內(nèi)存中得到的變量值放到工作內(nèi)存的變量副本中

use(使用):作用于工作內(nèi)存的變量,它把工作內(nèi)存的變量的值傳遞給執(zhí)行引擎

assign(賦值):作用于工作內(nèi)存的變量,它把一個(gè)從執(zhí)行引擎接收到的值賦給工作內(nèi)存中的變量

store(存儲(chǔ)):作用于工作內(nèi)存的變量,它把工作內(nèi)存中的一個(gè)變量的值傳送到主內(nèi)存中,以便隨后的write操作使用

wirte(寫(xiě)入):作用于主內(nèi)存,它把store操作從工作內(nèi)存中得到的變量的值放入主內(nèi)存的變量中。

如果我們要把一個(gè)變量從主內(nèi)存賦值到工作內(nèi)存,那就要順序執(zhí)行read和load;如果要從工作內(nèi)存同步回主內(nèi)存,就要順序執(zhí)行store和write操作【普通變量】。【注意,這里只是要求了順序執(zhí)行,而不是連續(xù)執(zhí)行,也就是說(shuō),中間是可以插入指令的】

那我們就知道了,++i非線程安全的原因是:

如果線程A在工作內(nèi)存中修改完成,準(zhǔn)備同步到主內(nèi)存,線程B剛從主內(nèi)存中讀取到,那么線程B讀取到的就是臟數(shù)據(jù),很容易會(huì)造成數(shù)據(jù)覆蓋。

此處要提到一個(gè)關(guān)鍵字:volatile

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

第一是保證此變量對(duì)所有線程的可見(jiàn)性(是指當(dāng)一個(gè)線程修改了這個(gè)變量的值,新值對(duì)于其他線程來(lái)說(shuō)是可以立即得知的),無(wú)法保證并發(fā)安全。

如題:如果把變量 i 加上 volatile也是無(wú)法保證線程安全的

原因:把變量的值取到棧頂,這個(gè)時(shí)候volatile關(guān)鍵字保證了值是正確的,但是在執(zhí)行++執(zhí)行時(shí),其他線程有可能把變量的值更新了,所以棧頂?shù)闹稻妥兂闪诉^(guò)期的數(shù)據(jù),再把數(shù)據(jù)同步到主內(nèi)存的時(shí)候就會(huì)造成數(shù)據(jù)覆蓋。

第二是禁止指令重排序優(yōu)化,保證變量賦值操作的順序與程序代碼中的執(zhí)行順序一致。重排序優(yōu)化是機(jī)器級(jí)的優(yōu)化操作,提前執(zhí)行是指這句話對(duì)應(yīng)的匯編代碼被提前執(zhí)行。

DCL(Double Check Lock)單例:

public classSingleton {private volatile staticSingleton singleton;public staticSingleton getInstance(){if (singleton == null){synchronized (Singleton.class){

singleton= newSingleton();

}

}returnsingleton;

}

}

那么怎么才能使++i線程安全呢?

有很多種方法,比如:synchronized、ReentrantLock...

今天要說(shuō)的是:java提供了一個(gè)原子類(lèi):AtomicInteger,AtomicInteger有什么方法就不一一介紹了,我們主要看一個(gè)方法的源碼:getAndIncrement

/*** Atomically increments by one the current value.

*

*@returnthe previous value*/

public final intgetAndIncrement() {for(;;) {int current = get(); //當(dāng)前的值 這里的 value也是volatile的,所以每次取都是最新的

int next = current + 1;if (compareAndSet(current, next)) //關(guān)鍵是這里

returncurrent;

}

}

//這是一個(gè)原子方法,第一個(gè)參數(shù)是期望的值(也可以理解為原來(lái)的值)//第二個(gè)參數(shù)是想要更新的值//過(guò)程:如果主內(nèi)存中當(dāng)前的值跟expect相等,那就直接更新, 返回true//否則就不更新,返回false//我們也看到getAndIncrement中是一個(gè)死循環(huán),發(fā)現(xiàn)更新失敗后會(huì)繼續(xù)循環(huán),知道更新成功。(CAS樂(lè)觀鎖)

public final boolean compareAndSet(int expect, intupdate) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);

}

以上是對(duì)++i的簡(jiǎn)單理解。

總結(jié)

以上是生活随笔為你收集整理的java i 线程不安全_java中的++i是线程安全的吗?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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