樂觀鎖和悲觀鎖是一種概念,他們的區別主要是在對待線程同步時的態度。java
synchronized
關鍵字和Lock
的實現類都屬於悲觀鎖。正是由於樂觀鎖和悲觀鎖的不一樣,他們所適用的場景天然不同,算法
//=============== 悲觀鎖 ===============
//synchronized
public synchronized void test() {
//須要同步的資源
}
/** * ReentrantLock * 須要保證多線程操做的是同一個鎖 */
ReentrantLock lock = new ReentrantLock();
public void test1() {
lock.lock();
try {
//須要同步的資源
} finally {
lock.unlock();
}
}
//=============== 樂觀鎖 ===============
/** * 須要保證多線程操做的是同一個AtomicInteger */
AtomicInteger atomicInteger = new AtomicInteger(0);
public void test2() {
atomicInteger.getAndIncrement();
}
複製代碼
經過上述使用方式咱們能夠總結出,悲觀鎖都是經過顯式調用去獲取鎖從而同步數據,可是爲何樂觀鎖不須要顯示的獲取鎖也一樣能同步數據呢。這裏就要談談什麼是CAS。安全
從字面意思上看,即比較和交換,是一種無鎖的算法。便可以在不須要加鎖的狀況下,實現多線程變量同步。在Java中,atomic
包下的原子類們是CAS的一系列實現多線程
其中,咱們就以最多見的AtomicInteger分析,源碼以下,併發
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
//反射獲取AtomicInteger類中value值的偏移量
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
//經過volatile關鍵字防止cpu指令重排序
//使value對全部線程可見
private volatile int value;
...
public final int getAndIncrement() {
//實際調用的是Unsafe.getAndAddInt
return unsafe.getAndAddInt(this, valueOffset, 1);
}
}
//Unsafe類
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
//經過循環重試比較新值與舊值,直到二者相等說明此時數據未被其餘線程修改,以後更新內存中的變量值
do {
var5 = this.getIntVolatile(var1, var2);
//compareAndSwapInt這個方法是native方法具體分析見底下
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
//native方法
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
複製代碼
可見實際的CAS操做的實現是在native層的compareAndSwapInt()
中,JNI裏是藉助於CPU指令cmpxchg
完成的,該指令是一個原子操做。顯然,能夠保證變量的可見性。高併發
具體CPU的cmpxchg
指令作的事情是,比較寄存器中的A和內存中的值V。ui
以後經過上述的do-while
循環再次調用cmpxchg
指令進行重試,一直到更新成功爲止。this
CAS這種算法雖然很是高效,但也存在問題。atom
public class ReentrantLock implements Lock, java.io.Serializable {
...
public ReentrantLock() {
//可見ReentrantLock默認使用的是非公平鎖
sync = new NonfairSync();
}
...
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
//非公平鎖的實現
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
...
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
//公平鎖的實現
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
...
}
protected final boolean tryAcquire(int acquires) {
...
}
}
}
複製代碼
咱們觀察到實際獲取鎖的邏輯在tryAcquire
方法中,咱們對NonfairSync
和FairSync
中(左爲FairSync
)該方法作橫向比較來看看他們的區別是什麼,spa
除了增長了hasQueuedPredecessors
之外沒有什麼不一樣,
public final boolean hasQueuedPredecessors() {
...
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
複製代碼
該方法主要是判斷當前線程是否位於同步隊列中的第一個。若是是則返回true,不然返回false。
synchronized
和Lock
的實現類都屬於互斥鎖。Java中ReentrantReadWriteLock
類實現了互斥鎖與共享鎖,以下
ReentrantReadWriteLock
有兩把鎖,ReadLock讀鎖,是共享鎖,WriteLock寫鎖,是互斥鎖。