ReentrantLock底层原理之AQS详解
在java中,好多时候需要考虑线程安全问题,我们一般选择是使用Sychornized或JUC中Lock接口的实现类来实现
Sychornized底层原理是JVM层面的,而Lock实现类ReentrantLock实现线程独占是为什么原理呢?下面我们将按这个思路去理解AQS原理,本篇不会搬太多AQS理论主要以ReentrantLock来理解AQS
ReentrantLock的简单应用
public void test() {
Lock lock = new ReentrantLock();
try {
lock.lock();
//业务代码执行
}catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
上面简单代码不做解释,通过代码看问题
- lock()方法是如何实现加锁的?
- 竞争失败的线程是如何实现等待及唤醒的?
- unlock()是怎样释放锁?
加锁过程
ReentrantLock 默认为非公平锁,里面实际上是定义了两个静态内部类来区别,以下分析非公平锁, 直接上源码,部分源码,其余省略
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
abstract void lock();
//略
}
public void lock() {
sync.lock();
}
static final class NonfairSync extends Sync {
final void lock() {
//Unsafe类的CAS操作,将状态值改为1
if (compareAndSetState(0, 1))
//设置当前线程为独占模式拥有者,相当于你抢到锁,我记录一下你
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
}
通过源码可知,真正实现加锁的是Sync抽象内部类,该类继承了AbstractQueuedSynchronizer ,呐,今天的主角出场,下面将进入AQS的源码分析
概述AbstractQueuedSynchronizer
理论部分不做太多概述,百度查的会很详细,以下介绍一下内部虚拟双向队列的原理,及重要的属性
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
//同步状态,0:未被占有,1:已被占有
private volatile int state;
//等待队列头节点
private transient volatile Node head;
//等待队列尾结点
private transient volatile Node tail;
//等待对列
static final class Node {
//等待状态 四个值:CANCELLED =1;SIGNAL= -1;CONDITION = -2;PROPAGATE = -3;0
volatile int waitStatus;
//前节点,前指针
volatile Node prev;
//后节点,后指针
volatile Node next;
//排队的线程
volatile Thread thread;
}
}
贴个图,便于理解:
acquire()
AQS中定义的方法
表示当前已经有线程占有,处于尝试获得状态,或者加入等待队列
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//中断线程,即尝试获取失败后加入等待队列,然后中断线程
selfInterrupt();
}
tryAcquire()尝试加锁
//尝试获取
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {//其他线程执行完或没有线程执行
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//当前占有的线程是自己,我觉得这里是可重入性的原因
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
addWaiter()加入队列,进行等待
尝试加锁失败后,则将当前线程加入队列,进行等待排队
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {//插入node
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
//尾结点为空时在这里插入,会新建一个哨兵节点
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
需要注意的是,enq()中compareAndSetHead(new Node()),这里是当队列为空时,新建一个空node节点,即上图中的哨兵节点
网上好多写的是第一个节点为当前占有线程的节点,这里我觉得第一个为空节点(个人理解,错误请指教);
acquireQueued()在队列中自旋等待
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {//队列中除哨兵节点外只剩该节点,且获取成功则删除节点,出队
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire方法是自旋中的信号控制,不会一直自旋,一定程度后会将线程阻塞,这里需要介绍一下parkAndCheckInterrupt方法,就是将线程进行阻塞
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
这里对LockSupport做个简单描述
LockSupport
我认为这是Sychornized和Lock的加强版,加锁解锁方式和Lock类似,为park(),unpark();
关于这一块的详细描述,后面会在写一篇
释放及唤醒锁过程
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)//队列中还有节点
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
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;
}
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);//唤醒线程
}
公平锁和非公平锁底层区别
非公平锁
公平锁
由上可以看出:唯一区别在于hasQueuedPredecessors()方法判断
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
手写ReentrantLock锁
调用AQS内部方法,重写AQS的tryAcquire、tryRelease即可
public class myReentrantLock extends AbstractQueuedSynchronizer {
public void lock(){
if (compareAndSetState(0,1)) {
setExclusiveOwnerThread(Thread.currentThread());
}else {
acquire(1);
}
}
public void unlock() {
release(1);
}
private boolean tryAcquireLoc(int arg) {
final Thread thread = Thread.currentThread();
int state = getState();
if (state == 0) {
if (compareAndSetState(0,arg)) {
setExclusiveOwnerThread(thread);
return true;
}
}else if (thread == getExclusiveOwnerThread()) {
int c = state + arg;
if (c < 0){
throw new RuntimeException();
}
setState(c);
return true;
}
return false;
}
@Override
protected boolean tryAcquire(int arg) {
return tryAcquireLoc(arg);
}
@Override
protected boolean tryRelease(int arg) {
int state = getState() - arg;
if (Thread.currentThread() != getExclusiveOwnerThread()) {
throw new RuntimeException("都不是你锁的,你凭什么解锁!!!");
}
boolean flag = false;
if (state == 0) {
setExclusiveOwnerThread(null);
flag = true;
}
setState(state);
return true;
}
}