摘要:三關(guān)鍵字能保證原子性嗎并發(fā)編程藝術(shù)這本書(shū)上說(shuō)保證但是在自增操作非原子操作上不保證,多線程編程核心藝術(shù)這本書(shū)說(shuō)不保證。多線程訪問(wèn)關(guān)鍵字不會(huì)發(fā)生阻塞,而關(guān)鍵字可能會(huì)發(fā)生阻塞關(guān)鍵字能保證數(shù)據(jù)的可見(jiàn)性,但不能保證數(shù)據(jù)的原子性。
系列文章傳送門(mén):
Java多線程學(xué)習(xí)(一)Java多線程入門(mén)
Java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(1)
java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(2)
Java多線程學(xué)習(xí)(三)volatile關(guān)鍵字
Java多線程學(xué)習(xí)(四)等待/通知(wait/notify)機(jī)制
Java多線程學(xué)習(xí)(五)線程間通信知識(shí)點(diǎn)補(bǔ)充
Java多線程學(xué)習(xí)(六)Lock鎖的使用
Java多線程學(xué)習(xí)(七)并發(fā)編程中一些問(wèn)題
系列文章將被優(yōu)先更新于微信公眾號(hào)“Java面試通關(guān)手冊(cè)”,歡迎廣大Java程序員和愛(ài)好技術(shù)的人員關(guān)注。
本節(jié)思維導(dǎo)圖:
思維導(dǎo)圖源文件+思維導(dǎo)圖軟件關(guān)注微信公眾號(hào):“Java面試通關(guān)手冊(cè)”回復(fù)關(guān)鍵字:“Java多線程”免費(fèi)領(lǐng)取。
一 簡(jiǎn)介 先來(lái)看看維基百科對(duì)“volatile關(guān)鍵字”的定義:在程序設(shè)計(jì)中,尤其是在C語(yǔ)言、C++、C#和Java語(yǔ)言中,使用volatile關(guān)鍵字聲明的變量或?qū)ο笸ǔ>哂信c優(yōu)化、多線程相關(guān)的特殊屬性。通常,volatile關(guān)鍵字用來(lái)阻止(偽)編譯器認(rèn)為的無(wú)法“被代碼本身”改變的代碼(變量/對(duì)象)進(jìn)行優(yōu)化。如在C語(yǔ)言中,volatile關(guān)鍵字可以用來(lái)提醒編譯器它后面所定義的變量隨時(shí)有可能改變,因此編譯后的程序每次需要存儲(chǔ)或讀取這個(gè)變量的時(shí)候,都會(huì)直接從變量地址中讀取數(shù)。如果沒(méi)有volatile關(guān)鍵字,則編譯器可能優(yōu)化讀取和存儲(chǔ),可能暫時(shí)使用寄存器中的值,如果這個(gè)變量由別的程序更新了的話,將出現(xiàn)不一致的現(xiàn)象。據(jù)
在C環(huán)境中,volatile關(guān)鍵字的真實(shí)定義和適用范圍經(jīng)常被誤解。雖然C++、C#和Java都保留了C中的volatile關(guān)鍵字,但在這些編程語(yǔ)言中volatile的用法和語(yǔ)義卻大相徑庭。
Java中的“volatile關(guān)鍵字”關(guān)鍵字:在 JDK1.2 之前,Java的內(nèi)存模型實(shí)現(xiàn)總是從主存(即共享內(nèi)存)讀取變量,是不需要進(jìn)行特別的注意的。而在當(dāng)前的 Java 內(nèi)存模型下,線程可以把變量保存本地內(nèi)存(比如機(jī)器的寄存器)中,而不是直接在主存中進(jìn)行讀寫(xiě)。這就可能造成一個(gè)線程在主存中修改了一個(gè)變量的值,而另外一個(gè)線程還繼續(xù)使用它在寄存器中的變量值的拷貝,造成數(shù)據(jù)的不一致。
要解決這個(gè)問(wèn)題,就需要把變量聲明為 volatile,這就指示 JVM,這個(gè)變量是不穩(wěn)定的,每次使用它都到主存中進(jìn)行讀取。
volatile 修飾的成員變量在每次被線程訪問(wèn)時(shí),都強(qiáng)迫從主存(共享內(nèi)存)中重讀該成員變量的值。而且,當(dāng)成員變量發(fā)生變化時(shí),強(qiáng)迫線程將變化值回寫(xiě)到主存(共享內(nèi)存)。這樣在任何時(shí)刻,兩個(gè)不同的線程總是看到某個(gè)成員變量的同一個(gè)值,這樣也就保證了同步數(shù)據(jù)的可見(jiàn)性。
RunThread.java
private boolean isRunning = true; int m; public boolean isRunning() { return isRunning; } public void setRunning(boolean isRunning) { this.isRunning = isRunning; } @Override public void run() { System.out.println("進(jìn)入run了"); while (isRunning == true) { int a=2; int b=3; int c=a+b; m=c; } System.out.println(m); System.out.println("線程被停止了!"); } }
Run.java
public class Run { public static void main(String[] args) throws InterruptedException { RunThread thread = new RunThread(); thread.start(); Thread.sleep(1000); thread.setRunning(false); System.out.println("已經(jīng)賦值為false"); } }
運(yùn)行結(jié)果:
RunThread類(lèi)中的isRunning變量沒(méi)有加上volatile關(guān)鍵字時(shí),運(yùn)行以上代碼會(huì)出現(xiàn)死循環(huán),這是因?yàn)閕sRunning變量雖然被修改但是沒(méi)有被寫(xiě)到主存中,這也就導(dǎo)致該線程在本地內(nèi)存中的值一直為true,這樣就導(dǎo)致了死循環(huán)的產(chǎn)生。
解決辦法也很簡(jiǎn)單:isRunning變量前加上volatile關(guān)鍵字即可。
這樣運(yùn)行就不會(huì)出現(xiàn)死循環(huán)了。
加上volatile關(guān)鍵字后的運(yùn)行結(jié)果:
你是不是以為到這就完了?
不存在的!?。?/strong>(這里還有一點(diǎn)需要強(qiáng)調(diào),下面的內(nèi)容一定要看,不然你在用volatile關(guān)鍵字時(shí)會(huì)很迷糊,因?yàn)闀?shū)籍幾乎都沒(méi)有提這個(gè)問(wèn)題)
假如你把while循環(huán)代碼里加上任意一個(gè)輸出語(yǔ)句或者sleep方法你會(huì)發(fā)現(xiàn)死循環(huán)也會(huì)停止,不管isRunning變量是否被加上了上volatile關(guān)鍵字。
加上輸出語(yǔ)句:
while (isRunning == true) { int a=2; int b=3; int c=a+b; m=c; System.out.println(m); }
加上sleep方法:
while (isRunning == true) { int a=2; int b=3; int c=a+b; m=c; try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
這是為什么呢?
因?yàn)椋篔VM會(huì)盡力保證內(nèi)存的可見(jiàn)性,即便這個(gè)變量沒(méi)有加同步關(guān)鍵字。換句話說(shuō),只要CPU有時(shí)間,JVM會(huì)盡力去保證變量值的更新。這種與volatile關(guān)鍵字的不同在于,volatile關(guān)鍵字會(huì)強(qiáng)制的保證線程的可見(jiàn)性。而不加這個(gè)關(guān)鍵字,JVM也會(huì)盡力去保證可見(jiàn)性,但是如果CPU一直有其他的事情在處理,它也沒(méi)辦法。最開(kāi)始的代碼,一直處于死循環(huán)中,CPU處于一直占用的狀態(tài),這個(gè)時(shí)候CPU沒(méi)有時(shí)間,JVM也不能強(qiáng)制要求CPU分點(diǎn)時(shí)間去取最新的變量值。而加了輸出或者sleep語(yǔ)句之后,CPU就有可能有時(shí)間去保證內(nèi)存的可見(jiàn)性,于是while循環(huán)可以被終止。
三 volatile關(guān)鍵字能保證原子性嗎?《Java并發(fā)編程藝術(shù)》這本書(shū)上說(shuō)保證但是在自增操作(非原子操作)上不保證,《Java多線程編程核心藝術(shù)》這本書(shū)說(shuō)不保證。
我個(gè)人更傾向于這種說(shuō)法:volatile無(wú)法保證對(duì)變量原子性的。我個(gè)人感覺(jué)《Java并發(fā)編程藝術(shù)》這本書(shū)上說(shuō)volatile關(guān)鍵字保證原子性嗎但是在自增操作(非原子操作)上不保證這種說(shuō)法是有問(wèn)題的。只是個(gè)人看法,希望不要被噴??梢钥聪旅鏈y(cè)試代碼:
MyThread.java
public class MyThread extends Thread { volatile public static int count; private static void addCount() { for (int i = 0; i < 100; i++) { count=i; } System.out.println("count=" + count); } @Override public void run() { addCount(); } }
Run.java
public class Run { public static void main(String[] args) { MyThread[] mythreadArray = new MyThread[100]; for (int i = 0; i < 100; i++) { mythreadArray[i] = new MyThread(); } for (int i = 0; i < 100; i++) { mythreadArray[i].start(); } } }
運(yùn)行結(jié)果:
上面的“count=i;”是一個(gè)原子操作,但是運(yùn)行結(jié)果大部分都是正確結(jié)果99,但是也有部分不是99的結(jié)果。
解決辦法:
使用synchronized關(guān)鍵字加鎖。(這只是一種方法,Lock和AtomicInteger原子類(lèi)都可以,因?yàn)橹皩W(xué)過(guò)synchronized關(guān)鍵字,所以我們使用synchronized關(guān)鍵字的方法)
修改MyThread.java如下:
public class MyThread extends Thread { public static int count; synchronized private static void addCount() { for (int i = 0; i < 100; i++) { count=i; } System.out.println("count=" + count); } @Override public void run() { addCount(); } }
這樣運(yùn)行輸出的count就都為99了,所以要保證數(shù)據(jù)的原子性還是要使用synchronized關(guān)鍵字。
四 synchronized關(guān)鍵字和volatile關(guān)鍵字比較volatile關(guān)鍵字是線程同步的輕量級(jí)實(shí)現(xiàn),所以volatile性能肯定比synchronized關(guān)鍵字要好。但是volatile關(guān)鍵字只能用于變量而synchronized關(guān)鍵字可以修飾方法以及代碼塊。synchronized關(guān)鍵字在JavaSE1.6之后進(jìn)行了主要包括為了減少獲得鎖和釋放鎖帶來(lái)的性能消耗而引入的偏向鎖和輕量級(jí)鎖以及其它各種優(yōu)化之后執(zhí)行效率有了顯著提升,實(shí)際開(kāi)發(fā)中使用synchronized關(guān)鍵字還是更多一些。
多線程訪問(wèn)volatile關(guān)鍵字不會(huì)發(fā)生阻塞,而synchronized關(guān)鍵字可能會(huì)發(fā)生阻塞
volatile關(guān)鍵字能保證數(shù)據(jù)的可見(jiàn)性,但不能保證數(shù)據(jù)的原子性。synchronized關(guān)鍵字兩者都能保證。
volatile關(guān)鍵字用于解決變量在多個(gè)線程之間的可見(jiàn)性,而ynchronized關(guān)鍵字解決的是多個(gè)線程之間訪問(wèn)資源的同步性。
參考:
《Java多線程編程核心技術(shù)》
《Java并發(fā)編程的藝術(shù)》
極客學(xué)院Java并發(fā)編程wiki: http://wiki.jikexueyuan.com/p...
如果你覺(jué)得博主的文章不錯(cuò),歡迎轉(zhuǎn)發(fā)點(diǎn)贊。你能從中學(xué)到知識(shí)就是我最大的幸運(yùn)。
歡迎關(guān)注我的微信公眾號(hào):“Java面試通關(guān)手冊(cè)”(分享各種Java學(xué)習(xí)資源,面試題,以及企業(yè)級(jí)Java實(shí)戰(zhàn)項(xiàng)目回復(fù)關(guān)鍵字免費(fèi)領(lǐng)取)。另外我創(chuàng)建了一個(gè)Java學(xué)習(xí)交流群(群號(hào):174594747),歡迎大家加入一起學(xué)習(xí),這里更有面試,學(xué)習(xí)視頻等資源的分享。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69101.html
摘要:今天開(kāi)始整理學(xué)習(xí)多線程的知識(shí),談?wù)勛钪匾膬蓚€(gè)關(guān)鍵字和。但是這樣一個(gè)過(guò)程比較慢,在使用多線程的時(shí)候就會(huì)出現(xiàn)問(wèn)題。有序性有序性是指多線程執(zhí)行結(jié)果的正確性。這種機(jī)制在多線程中會(huì)出現(xiàn)問(wèn)題,因此可以通過(guò)來(lái)禁止重排。 今天開(kāi)始整理學(xué)習(xí)多線程的知識(shí),談?wù)勛钪匾膬蓚€(gè)關(guān)鍵字:volatile和synchronized。 一、三個(gè)特性 1、原子性 所謂原子性操作就是指這些操作是不可中斷的,要么執(zhí)行過(guò)程...
摘要:當(dāng)一個(gè)線程持有重量級(jí)鎖時(shí),另外一個(gè)線程就會(huì)被直接踢到同步隊(duì)列中等待。 java代碼先編譯成字節(jié)碼,字節(jié)碼最后編譯成cpu指令,因此Java的多線程實(shí)現(xiàn)最終依賴(lài)于jvm和cpu的實(shí)現(xiàn) synchronized和volatile 我們先來(lái)討論一下volatile關(guān)鍵字的作用以及實(shí)現(xiàn)機(jī)制,每個(gè)線程看到的用volatile修飾的變量的值都是最新的,更深入的解釋就涉及到Java的內(nèi)存模型了,我們...
摘要:是需要我們?nèi)ヌ幚砗芏嗍虑?,為了防止多線程給我們帶來(lái)的安全和性能的問(wèn)題下面就來(lái)簡(jiǎn)單總結(jié)一下我們需要哪些知識(shí)點(diǎn)來(lái)解決多線程遇到的問(wèn)題。 前言 不小心就鴿了幾天沒(méi)有更新了,這個(gè)星期回家咯。在學(xué)校的日子要努力一點(diǎn)才行! 只有光頭才能變強(qiáng) 回顧前面: 多線程三分鐘就可以入個(gè)門(mén)了! Thread源碼剖析 本文章的知識(shí)主要參考《Java并發(fā)編程實(shí)戰(zhàn)》這本書(shū)的前4章,這本書(shū)的前4章都是講解并發(fā)的基...
摘要:一接觸內(nèi)存模型中的實(shí)例靜態(tài)變量以及數(shù)組都存儲(chǔ)在堆內(nèi)存中,可在線程之間共享。所以,在編碼上實(shí)現(xiàn)鎖的內(nèi)存語(yǔ)義,可以通過(guò)對(duì)一個(gè)變量的讀寫(xiě),來(lái)實(shí)現(xiàn)線程之間相互通知,保證臨界區(qū)域代碼的互斥執(zhí)行。 原文發(fā)表于我的博客 volatile關(guān)鍵字: 使用volatile關(guān)鍵字修飾的的變量,總能看到任意線程對(duì)它最后的寫(xiě)入,即總能保證任意線程在讀寫(xiě)volatile修飾的變量時(shí),總是從內(nèi)存中讀取最新的值。以下...
摘要:支持多線程,中創(chuàng)建線程的方式有兩種繼承類(lèi),重寫(xiě)方法。多線程編程很常見(jiàn)的情況下是希望多個(gè)線程共享資源,通過(guò)多個(gè)線程同時(shí)消費(fèi)資源來(lái)提高效率,但是新手一不小心很容易陷入一個(gè)編碼誤區(qū)。所以,在進(jìn)行多線程編程的時(shí)候一定要留心多個(gè)線程是否共享資源。 文章首發(fā)于 http://jaychen.cc作者 JayChen showImg(https://segmentfault.com/img/remo...
閱讀 3685·2021-10-11 11:09
閱讀 1349·2021-09-24 10:35
閱讀 3441·2021-07-29 13:48
閱讀 473·2019-08-30 13:15
閱讀 2526·2019-08-30 12:53
閱讀 3221·2019-08-30 12:44
閱讀 2718·2019-08-29 16:57
閱讀 968·2019-08-29 12:26