乐观锁和悲观锁是一种概念,他们的区别主要是在对待线程同步时的态度。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写锁,是互斥锁。