1.Synchronized
關鍵字
a.synchronized加在方法的前部
public synchronized void synMethod() {
//方法體
}
調用用關鍵字synchronized聲明的方法是排隊運行的。但假如線程A持有某對象的鎖,那線程B異步調用非synchronized類型的方法不受限制。
b. synchronized同步代碼塊
synchronized(this)
c.將任意對象作爲監視器
synchronized(非this對象)
d.靜態同步
(1)synchronize(class)
(2)在靜態方法前面加上synchronized關鍵字
小結:
a.synchronized取得的鎖都是對象鎖,而不是把一段代碼或方法當作鎖,在線程的執行過程當中,哪個線程先執行了帶synchronized關鍵字的方法,哪個線程就持有該方法所屬對象的鎖,那麼其他線程只能等待,前提是多個線程訪問的是同一個對象的synchronized方法。如果多個線程訪問多個對象,則JVM會創建多個鎖。
b.如果一個線程已經持有了一個對象的鎖,另外一個線程則不能訪問該對象的同步方法,但是可以訪問該對象的非同步方法。
c.同步不具有繼承性,如果父類的某個方法擁有synchronized關鍵字,子類的該方法若想同步必須手動的加入synchronized關鍵字;
d.關鍵字加到static方法上是給Class上鎖,加到非static是給對象上鎖。
class鎖可以對類的所有實例起作用,即就是在任何時候,只能有一個線程訪問該類的static方法。因爲static方法就是類方法。
Class鎖 鎖住以後,以實例對象爲參數的線程可以獲取到實例對象中的方法。比如User.Class鎖住後,以user1 user2 user3 爲參數的線程還是能獲取到相應對象的鎖。可以理解爲鎖對象是按照地址來判斷鎖住的是不是同一個對象。
線程的私有堆棧
2. volatile關鍵字
主要作用是使變量在多個線程間可見。加volatile關鍵字可強制性從公共堆棧進行取值,而不是從線程私有數據棧中取得變量的值。
小陷阱
將JVM設置爲-server模式,爲了線程運行的效率,會在線程的私有堆棧中取值,在不使用volatile關鍵字的時候,如果對共有堆棧中的值進行改變,會造成值的不統一。
讀取公共內存圖
Atomic
相關類
一個原子類型就是一個原子操作可用的類型,可在沒有鎖的情況下做到線程安全。但原子類也不是完全安全,雖然原子操作是安全的,可方法間的調用卻不是原子的,需要用同步。
synchronized和volatile比較
1)關鍵字volatile是線程同步的輕量級實現,性能比synchronized好,且volatile只能修飾變量,synchronized可修飾方法和代碼塊。
2)多線程訪問volatile不會發生阻塞,synchronized會出現阻塞
3)volatile能保證數據可見性,不保證原子性;synchronized可以保證原子性,也可以間接保證可見性,因爲synchronized會將私有內存和公共內存中的數據做同步。
4)volatile解決的是變量在多個線程間的可見性,synchronized解決的是多個線程訪問資源的同步性。
volatile
不具備同步性,也不具備原子性
如圖:
read and load 從主存複製變量到當前工作內存
use and assign 執行代碼,改變共享變量值
store and write 用工作內存數據刷新主存相關內容
其中use and assign 可以多次出現
但是這一些操作並不是原子性,也就是 在readload之後,如果主內存count變量發生修改之後,線程工作內存中的值由於已經加載,不會產生對應的變化,所以計算出來的結果會和預期不一樣
對於volatile修飾的變量,jvm虛擬機只是保證從主內存加載到線程工作內存的值是最新的
例如假如線程1,線程2 在進行read,load 操作中,發現主內存中count的值都是5,那麼都會加載這個最新的值
在線程1堆count進行修改之後,會write到主內存中,主內存中的count變量就會變爲6
線程2由於已經進行read,load操作,在進行運算之後,也會更新主內存count的變量值爲6
導致兩個線程及時用volatile關鍵字修改之後,還是會存在併發的情況。