国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

JAVA并發(fā)中VOLATILE關(guān)鍵字的神奇之處詳解

發(fā)布時(shí)間:2021-07-06 11:13 來(lái)源:腳本之家 閱讀:0 作者:statics 欄目: 開(kāi)發(fā)技術(shù)

并發(fā)編程中的三個(gè)概念:

1.原子性

在Java中,對基本數據類(lèi)型的變量的讀取和賦值操作是原子性操作,即這些操作是不可被中斷的,要么執行,要么不執行。

2.可見(jiàn)性

對于可見(jiàn)性,Java提供了volatile關(guān)鍵字來(lái)保證可見(jiàn)性。

  當一個(gè)共享變量被volatile修飾時(shí),它會(huì )保證修改的值會(huì )立即被更新到主存,當有其他線(xiàn)程需要讀取時(shí),它會(huì )去內存中讀取新值。

  而普通的共享變量不能保證可見(jiàn)性,因為普通共享變量被修改之后,什么時(shí)候被寫(xiě)入主存是不確定的,當其他線(xiàn)程去讀取時(shí),此時(shí)內存中可能還是原來(lái)的舊值,因此無(wú)法保證可見(jiàn)性。

  另外,通過(guò)synchronized和Lock也能夠保證可見(jiàn)性,synchronized和Lock能保證同一時(shí)刻只有一個(gè)線(xiàn)程獲取鎖然后執行同步代碼,并且在釋放鎖之前會(huì )將對變量的修改刷新到主存當中。因此可以保證可見(jiàn)性。

3.有序性

在Java內存模型中,允許編譯器和處理器對指令進(jìn)行重排序,但是重排序過(guò)程不會(huì )影響到單線(xiàn)程程序的執行,卻會(huì )影響到多線(xiàn)程并發(fā)執行的正確性。

  在Java里面,可以通過(guò)volatile關(guān)鍵字來(lái)保證一定的“有序性”(具體原理在下一節講述)。另外可以通過(guò)synchronized和Lock來(lái)保證有序性,很顯然,synchronized和Lock保證每個(gè)時(shí)刻是有一個(gè)線(xiàn)程執行同步代碼,相當于是讓線(xiàn)程順序執行同步代碼,自然就保證了有序性。

  另外,Java內存模型具備一些先天的“有序性”,即不需要通過(guò)任何手段就能夠得到保證的有序性,這個(gè)通常也稱(chēng)為 happens-before 原則。如果兩個(gè)操作的執行次序無(wú)法從happens-before原則推導出來(lái),那么它們就不能保證它們的有序性,虛擬機可以隨意地對它們進(jìn)行重排序。

volatile關(guān)鍵字的兩層語(yǔ)義

  一旦一個(gè)共享變量(類(lèi)的成員變量、類(lèi)的靜態(tài)成員變量)被volatile修飾之后,那么就具備了兩層語(yǔ)義:

  1)保證了不同線(xiàn)程對這個(gè)變量進(jìn)行操作時(shí)的可見(jiàn)性,即一個(gè)線(xiàn)程修改了某個(gè)變量的值,這新值對其他線(xiàn)程來(lái)說(shuō)是立即可見(jiàn)的。

  2)禁止進(jìn)行指令重排序。

  先看一段代碼,假如線(xiàn)程1先執行,線(xiàn)程2后執行:

//線(xiàn)程1
boolean stop = false;
while(!stop){
    doSomething();
}
 
//線(xiàn)程2
stop = true;

這段代碼是很典型的一段代碼,很多人在中斷線(xiàn)程時(shí)可能都會(huì )采用這種標記辦法。但是事實(shí)上,這段代碼會(huì )完全運行正確么?即一定會(huì )將線(xiàn)程中斷么?不一定,也許在大多數時(shí)候,這個(gè)代碼能夠把線(xiàn)程中斷,但是也有可能會(huì )導致無(wú)法中斷線(xiàn)程(雖然這個(gè)可能性很小,但是只要一旦發(fā)生這種情況就會(huì )造成死循環(huán)了)。

  下面解釋一下這段代碼為何有可能導致無(wú)法中斷線(xiàn)程。在前面已經(jīng)解釋過(guò),每個(gè)線(xiàn)程在運行過(guò)程中都有自己的工作內存,那么線(xiàn)程1在運行的時(shí)候,會(huì )將stop變量的值拷貝一份放在自己的工作內存當中。

  那么當線(xiàn)程2更改了stop變量的值之后,但是還沒(méi)來(lái)得及寫(xiě)入主存當中,線(xiàn)程2轉去做其他事情了,那么線(xiàn)程1由于不知道線(xiàn)程2對stop變量的更改,因此還會(huì )一直循環(huán)下去。

  但是用volatile修飾之后就變得不一樣了:

  第一:使用volatile關(guān)鍵字會(huì )強制將修改的值立即寫(xiě)入主存;

  第二:使用volatile關(guān)鍵字的話(huà),當線(xiàn)程2進(jìn)行修改時(shí),會(huì )導致線(xiàn)程1的工作內存中緩存變量stop的緩存行無(wú)效(反映到硬件層的話(huà),就是CPU的L1或者L2緩存中對應的緩存行無(wú)效);

  第三:由于線(xiàn)程1的工作內存中緩存變量stop的緩存行無(wú)效,所以線(xiàn)程1再次讀取變量stop的值時(shí)會(huì )去主存讀取。

  那么在線(xiàn)程2修改stop值時(shí)(當然這里包括2個(gè)操作,修改線(xiàn)程2工作內存中的值,然后將修改后的值寫(xiě)入內存),會(huì )使得線(xiàn)程1的工作內存中緩存變量stop的緩存行無(wú)效,然后線(xiàn)程1讀取時(shí),發(fā)現自己的緩存行無(wú)效,它會(huì )等待緩存行對應的主存地址被更新之后,然后去對應的主存讀取最新的值。

  那么線(xiàn)程1讀取到的就是最新的正確的值。

2.volatile保證原子性嗎?

  從上面知道volatile關(guān)鍵字保證了操作的可見(jiàn)性,但是volatile能保證對變量的操作是原子性嗎?

  下面看一個(gè)例子:

public  class  Test {
     public  volatile  int  inc =  0 ;
     
     public  void  increase() {
         inc++;
     }
     
     public  static  void  main(String[] args) {
         final  Test test =  new  Test();
         for ( int  i= 0 ;i< 10 ;i++){
             new  Thread(){
                 public  void  run() {
                     for ( int  j= 0 ;j< 1000 ;j++)
                         test.increase();
                 };
             }.start();
         }
         
         while (Thread.activeCount()> 1 )   //保證前面的線(xiàn)程都執行完
             Thread.yield();
         System.out.println(test.inc);
     }
}

  大家想一下這段程序的輸出結果是多少?也許有些朋友認為是10000。但是事實(shí)上運行它會(huì )發(fā)現每次運行結果都不一致,都是一個(gè)小于10000的數字。

  可能有的朋友就會(huì )有疑問(wèn),不對啊,上面是對變量inc進(jìn)行自增操作,由于volatile保證了可見(jiàn)性,那么在每個(gè)線(xiàn)程中對inc自增完之后,在其他線(xiàn)程中都能看到修改后的值啊,所以有10個(gè)線(xiàn)程分別進(jìn)行了1000次操作,那么最終inc的值應該是1000*10=10000。

  這里面就有一個(gè)誤區了,volatile關(guān)鍵字能保證可見(jiàn)性沒(méi)有錯,但是上面的程序錯在沒(méi)能保證原子性??梢?jiàn)性只能保證每次讀取的是最新的值,但是volatile沒(méi)辦法保證對變量的操作的原子性。

  在前面已經(jīng)提到過(guò),自增操作是不具備原子性的,它包括讀取變量的原始值、進(jìn)行加1操作、寫(xiě)入工作內存。那么就是說(shuō)自增操作的三個(gè)子操作可能會(huì )分割開(kāi)執行,就有可能導致下面這種情況出現:

  假如某個(gè)時(shí)刻變量inc的值為10,

  線(xiàn)程1對變量進(jìn)行自增操作,線(xiàn)程1先讀取了變量inc的原始值,然后線(xiàn)程1被阻塞了;

  然后線(xiàn)程2對變量進(jìn)行自增操作,線(xiàn)程2也去讀取變量inc的原始值,由于線(xiàn)程1只是對變量inc進(jìn)行讀取操作,而沒(méi)有對變量進(jìn)行修改操作,所以不會(huì )導致線(xiàn)程2的工作內存中緩存變量inc的緩存行無(wú)效,所以線(xiàn)程2會(huì )直接去主存讀取inc的值,發(fā)現inc的值時(shí)10,然后進(jìn)行加1操作,并把11寫(xiě)入工作內存,最后寫(xiě)入主存。

  然后線(xiàn)程1接著(zhù)進(jìn)行加1操作,由于已經(jīng)讀取了inc的值,注意此時(shí)在線(xiàn)程1的工作內存中inc的值仍然為10,所以線(xiàn)程1對inc進(jìn)行加1操作后inc的值為11,然后將11寫(xiě)入工作內存,最后寫(xiě)入主存。

  那么兩個(gè)線(xiàn)程分別進(jìn)行了一次自增操作后,inc只增加了1。

  解釋到這里,可能有朋友會(huì )有疑問(wèn),不對啊,前面不是保證一個(gè)變量在修改volatile變量時(shí),會(huì )讓緩存行無(wú)效嗎?然后其他線(xiàn)程去讀就會(huì )讀到新的值,對,這個(gè)沒(méi)錯。這個(gè)就是上面的happens-before規則中的volatile變量規則,但是要注意,線(xiàn)程1對變量進(jìn)行讀取操作之后,被阻塞了的話(huà),并沒(méi)有對inc值進(jìn)行修改。然后雖然volatile能保證線(xiàn)程2對變量inc的值讀取是從內存中讀取的,但是線(xiàn)程1沒(méi)有進(jìn)行修改,所以線(xiàn)程2根本就不會(huì )看到修改的值。

  根源就在這里,自增操作不是原子性操作,而且volatile也無(wú)法保證對變量的任何操作都是原子性的。

  在java 1.5的java.util.concurrent.atomic包下提供了一些原子操作類(lèi),即對基本數據類(lèi)型的 自增(加1操作),自減(減1操作)、以及加法操作(加一個(gè)數),減法操作(減一個(gè)數)進(jìn)行了封裝,保證這些操作是原子性操作。atomic是利用CAS來(lái)實(shí)現原子性操作的(Compare And Swap),CAS實(shí)際上是利用處理器提供的CMPXCHG指令實(shí)現的,而處理器執行CMPXCHG指令是一個(gè)原子性操作。

3.volatile能保證有序性嗎?

  在前面提到volatile關(guān)鍵字能禁止指令重排序,所以volatile能在一定程度上保證有序性。

  volatile關(guān)鍵字禁止指令重排序有兩層意思:

  1)當程序執行到volatile變量的讀操作或者寫(xiě)操作時(shí),在其前面的操作的更改肯定全部已經(jīng)進(jìn)行,且結果已經(jīng)對后面的操作可

最后一點(diǎn)

CAS原理

由于 volatile 關(guān)鍵字不具有原子性,所以一般在使用 volatile 關(guān)鍵字的地方,常常出現 CAS。

CAS是 Compare And Swap,它和 volatile 關(guān)鍵字都是實(shí)現 JUC 的基礎,其中 java.util.concurrent.atomic 核心都是 CAS 。

使用 CAS 有兩個(gè)核心參數,第一個(gè)是舊值,第二個(gè)是期望值。根據當前類(lèi)(this)和 內存偏移(valueOffset)計算出內存中的值,當內存中的值和舊值相等時(shí),更新為新值并返回 true ,否則返回 false。

比如 AtomicInteger 類(lèi)中的 CompareAndSet() 方法:

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

根據 this 和 valueOffset 計算出的值與 expect 是否相等,相等把內存中的值更新為 update 并返回 true ,否則返回 false 。

說(shuō)明了這些線(xiàn)程安全的包裝類(lèi)的底層都是用到了volatile關(guān)鍵字做線(xiàn)程安全的保證

總結

到此這篇關(guān)于JAVA并發(fā)中VOLATILE關(guān)鍵字神奇之處的文章就介紹到這了,更多相關(guān)JAVA并發(fā)VOLATILE關(guān)鍵字內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。

国产95在线 | 亚洲| 久久夜色精品国产嚕嚕亚洲AV| 在线а√天堂中文官网| 久久天天躁狠狠躁夜夜AV浪潮| 亚洲三级香港三级久久| 中文字幕欧美成人免费|