CAS
全稱:Compare and swap,能夠比較和交換某個寄存器中的值和內存中的值,看是否相等,如果相等,則把另外一個寄存器中的值和內存進行交換.
(這是一個偽代碼,所以這里的&address實際上是想要表示取出address中的值)
那么我們可以看到,CAS就是這樣一個簡單的交換操作,那么我們為什么要在這里單獨提出來呢?
原因是:這一段邏輯是通過一條CPU指令來完成的!(這就意味著,它是原子的)
這個對于編寫線程安全的代碼是非常重要的.我們為什么會有線程安全問題?歸根結底是因為當前操作不是原子的.基于CAS又可以衍生出一套"無鎖編程",但是CAS的使用范圍具有一定的局限性.加鎖的適用范圍更廣.
CAS的應用
實現原子類
比如,多線程針對一個count變量進行++操作,在Java標準庫中,已經提供了一組原子類
//原子類的使用
public class Demo {public static AtomicInteger count =new AtomicInteger();public static void main(String[] args) throws InterruptedException {Thread t1=new Thread(()->{for (int i=0;i<5000;i++){// count++;//注意,這里是一個對象,而不是一個變量,所以不能直接使用++;count.getAndIncrement();//相當于:count++;//count.incrementAndGet();//相當于:++count;//count.getAndDecrement();//相當于count--;//count.decrementAndGet();//相當于--count;}});Thread t2=new Thread(()->{for (int i = 0; i < 5000; i++) {count.getAndIncrement();}});t1.start();t2.start();t1.join();t2.join();System.out.println(count.get());}
}
對于這個關鍵字,現在這個關鍵字說的已經不算了,到底要不要將這個數據放到寄存器當中,是由編譯器來自主決定的.
為什么這兩個CAS訪問內存會有先后呢?多個CPU在操作同一個資源,也會涉及到鎖競爭(指令級別的鎖),指令級別的鎖是比synchronized代碼級別的鎖要輕量很多的.
實現自旋鎖
CAS的ABA問題
CAS的關鍵要點是比較寄存器1和內存的值,通過這里的值是否相等來判定內存的值是否發生了變化,如果內存的值變了,那么就存在其他線程對其進行了修改,如果內存的值沒有變,那么就說明沒有其他線程進行修改,接下來進行的修改就是安全的.
如果這里的值沒有變,就一定沒有別的線程進行修改嗎?
A-B-A
另一個線程,把變量的值從A->B,又從B->A.此時本線程就區分不了,這個值是始終沒變還是出現變化有變回來了的這種情況.大部分情況下,就算出現ABA問題也沒事.
在面對ABA問題,CAS的基本思路是正確的,但是主要是修改操作能夠進行反復橫跳,就容易讓咱們CAS的判定失效.CAS判定的是"值相同",實際上期望的是"值沒有變化過"如果約定,值只能單向變化,那不就可以判斷了嗎?有的時候我們的數據不允許只增加不減少,此時我們只需要在增加一個變量來專門記錄修改的次數就可以了.
CAS也是屬于多線程開發中的一種典型的思路,在實際開發中,一般不會直接使用CAS,都是用庫里已經基于CAS封裝好的組件(就像原子類這種)
native
見到native關鍵字,就要明白這個方法是在JVM內部通過C++代碼實現的了