一:分析独占锁
JUC对于资源的共享资源的分类分为2种:共享不可改变资源(fianl修饰)和共享可变资源。因此产生了JUC中的2种锁类型:共享锁和独占锁。
上一节我们简述了Lock接口以及其实现类ReentraintLock的源码以及常见用法。该Lock是一个独占锁的API,下面我们就给予该Lock接口做一些源码分析。
关于一些常用的说明
同步状态state:
线程被封装成Node节点的状态:
二:ReentrantLock源码分析独占锁(公平锁)
(1)ThreadA获取锁(lock())(第一个线程)
说明:由于A线程为第一个线程,所以返回结构为True,并且AQS的FIFO队列为NUll
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* 尝试获取锁
*
*/
protected final boolean tryAcquire(int acquires) {
//设置线程为当前线程
final Thread current = Thread.currentThread();
//获取到同步状态
int c = getState();
//c=0表示锁未被占用
if (c == 0) {
//当前线程没有其他线程,CAS更新同步状态
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
//设置为独占线程
setExclusiveOwnerThread(current);
return true;
}
}
//判断该线程是否重入
else if (current == getExclusiveOwnerThread()) {
//重入次数+1
int nextc = c + acquires;
if (nextc < 0)
//重入达到最大次数
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
(2)ThreadB尝试获取锁
由于锁被ThreadA占用,所以上面的tryAcquire()返回false。然后将Thread封装成一个节点。ThreadB获取锁有4个过程
- 获取当前锁失败(锁被A占用)
- 封装成Node节点插入FIFO队列
- 判断能否被唤醒
- 判断是否阻塞
/**
*将Node节点添加到AQS的FIFO队列中
*/
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
//获取到前一个节点
Node pred = tail;
//前一个节点存在(在介绍FIFO队列时,提到一个东西:在FIFO队列中前后节点用“属性”关联,方便
处理中断和超时)
if (pred != null) {
//当前当前节点的“前一个属性”为前一个节点
node.prev = pred;
if (compareAndSetTail(pred, node)) {
//前一个节点的“后一个节点属性”为当前节点
pred.next = node;
return node;
}
}
//前一个节点不存在,将当前节点插入到队列中
enq(node);
return node;
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
//当前节点可以被前一个节点唤醒
return true;
if (ws > 0) {
//当前节点抛出异常,移除
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//初始化节点状态0
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
(3)ThreadA释放锁(unlock())
ThreadA执行完毕,释放锁经历一下过程:
- 尝试释放锁
- 释放成功唤醒FIFO队列中的首节点进行执行
/**
*尝试释放锁
*/
protected final boolean tryRelease(int releases) {
//同步状态 -1
int c = getState() - releases;
//释放锁和持有锁的线程不是一个抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//所释放成功
if (c == 0) {
free = true;
//清除占有线程
setExclusiveOwnerThread(null);
}
//充值同步状态
setState(c);
return free;
}
(4)ThreadB被唤醒
ThreadB被唤醒,开始进行执行获取锁操作
- 判断Thread是否中断
- ThreadB开始获自旋换取到锁(就同之前的一样了了)
三:ReentrantLock源码分析独占锁(非公平锁)
四:分析
至此,以Lock分析锁独占功能就到这里了,实则独占功能就是依靠FIFO队列的CAS操作保证了队列的有序性和同步性。