之前说过,JDK有两种锁的方式,一种是Synchronized,一种就是CAS
基于CAS实现的Unsafe我们无法直接使用,我们一般就是使用ReentrantLock来实现锁的功能
那么本文我们就来看下ReentrantLock是如何实现CAS锁的
建议读者也看一下关于AbstractQueuedSynchronizer的源码解析,AQS作为一个基础类,ReentrantLock会用到里面的很多方法
可以参考下笔者的另一篇博客:JDK源码解析之AbstractQueuedSynchronizer
1.ReentrantLock结构分析
public class ReentrantLock implements Lock, java.io.Serializable {
// 只有这么一个成员变量
private final Sync sync;
// 两种构造方法
public ReentrantLock() {
// 默认是非公平锁
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
// 可以让用户自主选择公平锁或非公平锁
sync = fair ? new FairSync() : new NonfairSync();
}
1)有关于ReentrantLock.Sync
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
abstract void lock();
...
}
2)ReentrantLock.FairSync公平锁
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
...
}
}
3)ReentrantLock.NonFairSync非公平锁
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
总结:通过上面的结构分析可知
基础的实现类就是Sync,而Sync继承了AQS
非公平锁NonFairSync继承了Sync
公平锁FairSync也继承了Sync
4)ReentrantLock的基本使用
就是我们之前看过的ArrayBlockingQueue.offer(E e)
// ArrayBlockingQueue.offer(E e)
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
// 获取锁
lock.lock();
try {
if (count == items.length)
return false;
else {
enqueue(e);
return true;
}
} finally {
// 释放锁
lock.unlock();
}
}
由上例可知:ReentrantLock的使用还是比较简单的,lock()方法就是用来获取锁;unlock()就是用来释放锁。
下面来着重分析一下这两个方法(我们来分析默认的非公平锁)
2.ReentrantLock.lock()获取锁(非公平锁NonFairSync)
// ReentrantLock.lock()
public void lock() {
sync.lock();
}
// NonFairSync.lock()
final void lock() {
// 使用cas设置state为1
if (compareAndSetState(0, 1))
// 如果设置成功则置当前线程为独占线程
setExclusiveOwnerThread(Thread.currentThread());
else
// 如果设置state失败,说明已经被其他线程设置成功了,
// 则进入AQS.acquire方法
// 关于acquire的具体细节可以参考笔者关于AQS分析的博客
acquire(1);
}
// AbstractQueuedSynchronizer.compareAndSetState()
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
总结:NonFairSync.lock()方法还是比较简单的,直接就是使用CAS来设置AQS.state=1,成功则说明当前线程已经竞争到锁,可以继续下面的业务处理;如果失败,则说明当前线程竞争锁失败,需要进入队列等待锁被释放继续后面的竞争。
3.ReentrantLock.unlock()释放锁(非公平锁NonFairSync)
// ReentrantLock.unlock()
public void unlock() {
sync.release(1);
}
// NonFairSync.release()
// 直接就进入了AQS.release()
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
总结:ReentrantLock.unlock()直接就调用了AQS.release()
具体不再分析,读者可参考JDK源码解析之AbstractQueuedSynchronizer
4.ReentrantLock的其他加锁方式
1)ReentrantLock.tryLock()尝试锁,如果失败则直接返回,不再等待
// ReentrantLock.tryLock()
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
// Sync.nonfairTryAcquire()
final boolean nonfairTryAcquire(int acquires) {
// 1.获取当前线程及状态
final Thread current = Thread.currentThread();
int c = getState();
// state=0则意味着没有线程获取到锁,使用CAS将state设置为1,
// 返回true代表当前线程获取到锁
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 2.如果发现c!=0但是持有锁的线程是当前线程的话,
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
// state累加,如果
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?