首先,在jvm中有一個main memory,而每個線程都有自己的working memory,一個線程對一個variable進行操作的時候,會先在自己的working memory裡面建立一個copy,操作完成之後再寫入main memory,如果有多個線程同時操作同一個variable,就可能會出現不可預知的結果,所以線程安全就是為了避免這種情況的發生。在java中,確保線程安全的方法有兩種:一種是使用內置鎖,一種是使用原子類(java.util.concurrent包下的);
對於內置鎖:
可以同步方法,也可以同步代碼塊,不過建議同步代碼塊,因為同步方法會鎖住整個對象,哪怕這個類中有多個不相關的同步塊,這通常會導致他們停止執行並需要等待獲得這個對象上的鎖。
對於原子類:
鎖機制會存在以下問題:
1、在多線程競爭下,加鎖、釋放鎖都會導致比較多的上下文切換(即存儲和恢復cpu的狀態的過程)和調度延時,引起性能問題;
2、一個線程持有鎖會導致其它所有需要此鎖的線程掛起;
3、如果一個優先級高的線程等待一個優先級線程低的線程釋放鎖會導致優先級倒置,引起性能風險;
對於原子類,它所用的機制就是CAS機制(Compare And Swap),CAS有3個操作數,內存值V,舊的預期值A,要修改的新值B,當且僅當預期值A與內存值V相同時,才會將內存值V修改為B,否則什麼都不做;
現代cpu提供了特殊的指令,可以自動更新共享數據,而且能夠檢測到其他線程的干擾,而compareAndSet()就是用這些代替了鎖定,compareAndeSet()是調用本地方法來完成cpu指令的操作。
來看看AutomicInteger的源碼:
private volatile int value;//毫無疑問,沒有鎖的機制下,必須借助volatile保證線程間的數據可見性(volatile的原理是使線程直接讀取該變量並且不去緩存它,就確保了線程讀取到的變量是同一內存中的,保證一致性)
public final int get(){
return value;
}
來看看++i是怎麼實現的:
public final int increamentAndGet(){
for(;;){
int current = get();
int next = current + 1;
if(compareAndSet(current,next))
return next;
}
}