Atomic:
Atomic包是java.util.concurrent下的另一個專門為線程安全設計的java的包,包含多個原子性操作的類。基本特性就是在多線程情況下,當多個線程想要同時操作這些類的某些實例方法時,具有排他性,也就是當某個線程在執行某個方法時,不會被其他線程打斷,其他線程會在外部等待,一直等到該方法執行完畢,才由JVM從等待隊列中選擇另一個線程進入,這只是一種邏輯上的理解。實際上是借助硬件的相關指令來實現的,不會阻塞線程(只是在硬件級別去阻塞了)。可以對基本數據,數組中的基本數據,對類中的基本數據進行操作。原子變量類相當於一種泛化的volatile變量,能夠支持原子的和有條件的讀寫操作。
我們先看一下傳統的鎖是怎樣保證線程安全的
class LockDemo {
private int a;
public synchronized void setA(int b) {
this.a = b;
}
public synchronized int getA() {
return a;
}
public synchronized void addA() {
++a;
}
public synchronized void reduceA() {
--a;
}
}
其實這樣的synchronized已經能滿足我們日常的線程安全需求了,synchronized是基於代碼阻塞的機制,也就是當某個線程占用資源時,其他線程是無法進入的,如果這個線程出現問題的時候,出現大量線程阻塞,CPU就會耗費大量資源來處理阻塞在外的這些線程,但是CPU的任務本不該如此,還極可能出現死鎖等問題,對於這樣的簡單操作反而顯得有些笨重,所以應該有更合適更高效的方法來處理這樣的問題。所以就有了CAS
Compare and swap(CAS)
當前的處理器基本都支持CAS,這是一種基於硬件的處理,每一個CAS操作都包含三個運算符:內存地址V , 一個期望值A和新值B,操作的時候如果這個地址上存放的值等於期望的值A,那么就賦值為B,如果沒有,那么就不做任何操作。簡而言之,你的值和我的期望值相等,就給你新值,否則不干活了。
我們自己可以簡單的模擬一下CAS的實現
class CASDemo {
private int value;
public synchronized int getValue() {
return value;
}
public synchronized int cas(int expectValue , int newValue) {
int oldValue = value;
if(value == expectValue) {
value = newValue;
}
return oldValue;
}
}
class CASTest {
private CASDemo casDemo;
public int getValue () {
return casDemo.getValue();
}
public int add () {
int oldValue = casDemo.getValue();
while(casDemo.cas(oldValue , oldValue + 1) != oldValue) {
oldValue = casDemo.getValue();
}
return oldValue + 1;
}
}
看下Atomic包
看一下AtomicInteger類是怎么處理自增的處理的,也就是方法getAndIncrement()
這里調用了一個叫做Unsafe類的方法,這個類比較特殊內部大多是native的方法,而且這個類也不允許我們隨意使用,當然JDK自己是可以用的,(ps:處於凡是試試的態度,我試了一下調用它的方法,報錯如下)
看一下valueOffset是哪來的
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
這里又調用了Unsafe類的一個方法,參數是通過反射獲取的AtomicInteger的value屬性,也就是它的值
繼續進
public native long objectFieldOffset(Field var1);
很可惜,是一個本地方法,查看文檔可知,這個方法返回的就是"原來的值"的內存地址 , valueOffset的值(ps:文檔是其他某位大神那里借來的)
返回一開始的方法 點進去看
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
參數比較混亂 var2就是valueOffset 繼續跟進
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
喔,又是一個本地方法,參數很亂 按我們之前的講就是:
var2(valueOffset) , var4(expectValue) , var5(newValue)
我再借一點注釋
這個方法原子的將變量的值更新為var5,如果成功了返回true。這樣返回去取反即為false,循環結束,返回var5,取到新值。
當然Unsafe關於CAS的方法都是本地方法,是由C語言實現的,我們這里是看不到具體實現細節的。
說了半天 到底CAS是怎么保證線程安全的呢,其實在語言層次我們是沒有做任何關於同步的操作的,也沒有任何鎖。Atomic包下這些類將這些操作都交給了CPU和內存,利用CPU多處理的能力,實現硬件的阻塞,再加上volatile變量的特性即可實現基於原子性的操作的線程安全。所以CAS不是沒有阻塞 ,只是阻塞不是語言層面,而是在硬件層面,這樣便會更高效。