1. 程式人生 > >原子性、可見性、synchronized 有好理解

原子性、可見性、synchronized 有好理解

 

原子性、可見性、synchronized 有好理解:

from: https://blog.csdn.net/wohaqiyi/article/details/67635010

1、原子性

(1)原子是構成物質的基本單位(當然電子等暫且不論),所以原子的意思代表著——“不可分”;

(2)原子性是拒絕多執行緒操作的,不論是多核還是單核,具有原子性的量,同一時刻只能有一個執行緒來對它進行操作。簡而言之,在整個操作過程中不會被執行緒排程器中斷的操作,都可認為是原子性。例如 a=1是原子性操作,但是a++和a +=1就不是原子性操作。

2、非原子性

  (1)也就是整個過程中會出現執行緒排程器中斷操作的現象,例如:

類似"a += b"這樣的操作不具有原子性,在某些JVM中"a += b"可能要經過這樣三個步驟:

(1)取出a和b

(2)計算a+b

(3)將計算結果寫入記憶體

如果有兩個執行緒t1,t2在進行這樣的操作。t1在第二步做完之後還沒來得及把資料寫回記憶體就被執行緒排程器中斷了,於是t2開始執行,t2執行完畢後t1又把沒有完成的第三步做完。這個時候就出現了錯誤,相當於t2的計算結果被無視掉了。所以上面的買碘片例子在同步add方法之前,實際結果總是小於預期結果的,因為很多操作都被無視掉了。

類似的,像"a++"這樣的操作也都不具有原子性。所以在多執行緒的環境下一定要記得進行同步操作。

3、可見性

  可見性volatile修飾詞,可以應對多執行緒同時訪問修改同一變數,由於相互的不可見性所帶來的不可預期的結果,存在二義性的現象,出現的。

 多執行緒變數不可見:當一個執行緒對一變數a修改後,還沒有來得及將修改後的a值回寫到主存,而被執行緒排程器中斷操作(或收回時間片),然後讓另一執行緒進行對a變數的訪問修改,這時候,後來的執行緒並不知道a值已經修改過,它使用的仍舊是修改之前的a值,這樣修改後的a值就被另一執行緒覆蓋掉了。

多執行緒變數可見:被volatile修飾的成員變數在每次被執行緒訪問時,都強迫從記憶體中重讀該成員變數的值;而且,當成員變數發生變化時,強迫執行緒將變化值回寫到共享記憶體,這樣在任何時刻兩個不同執行緒總是看到某一成員變數的同一個值,這就是保證了可見性。

volatile使用場景:在兩個或者更多的執行緒訪問的成員變數上使用volatile。當要訪問的變數已在synchronized程式碼塊中,或者為常量時,不必使用。
由於使用volatile遮蔽掉了JVM中必要的程式碼優化,所以在效率上比較低,因此一定在必要時才使用此關鍵字。
注意:
如果給一個【變數加上volatile】修飾符,就相當於:每一個執行緒中一旦這個值發生了變化就馬上重新整理回主存,使得各個執行緒取出的值相同。編譯器不要對這個變數的讀、寫操作做優化。但是值得注意的是,除了對long和double的簡單操作之外,volatile並不能提供原子性。所以,就算你將一個變數修飾為volatile,但是對這個變數的操作並不是原子的,在併發環境下,還是不能避免錯誤的發生!

4、synchronized

         synchronized為一段操作或記憶體進行加鎖,它具有互斥性。當執行緒要操作被synchronized修飾的記憶體或操作時,必須首先獲得鎖才能進行後續操作;但是在同一時刻只能有一個執行緒獲得相同的一把鎖(物件監視器),所以它只允許一個執行緒進行操作。

 

 

 

from:https://ask.csdn.net/questions/345263?ref=myrecommend

======

 

總結: 

   鎖只能保證執行緒之間互相不衝突,不受對方的干擾。要保證執行緒的執行順序,本質上是需要排隊(佇列)的。

   ★原子性就是一組操作要麼全部做,要麼全不做,跟資料庫的選擇性一樣,類似事務的特徵。但是Java的原子性是由鎖來保證的。

  ★比如,你在ATM取款機取錢,ATM程式中吐錢跟在你賬戶上扣掉等額的數目就是一個原子性的操作,這兩個動作一定要連在一起操作,要麼都成功,要麼都失敗,不可以被分開只執行某一部分。