从一个自定义锁开始理解AQS
自定义锁
要使用一个自定义锁,要实现Lock接口,我们可以看到Lock中的方法都是基于Sync这个内部类的。所以自定义锁的时候我也创建了一内部类Sync,继承了AQS。
对于AQS,它提供了一堆模板方法,这些方法不需要我们实现,而只有5个方法我们可以重写,那就是:
- tryAcquire
- tryRelease
- tryAcquireShared
- tryReleaseShared
- isHeldExclusively
对于最基本的使用,其实只要重写tryAcquire和tryRelease就可以
ReentrantLock可重入锁
RenntrantLock其实就是一个实现了Lock接口的类。它所做的也是我们上诉所说的实现了一个自定义锁。
从源码上理解ReentrantLock加锁
static ReentrantLock lock = new ReentrantLock(); // 定义一个锁
lock.lock()
我们来尝试理解一下lock()这个方法它做了什么,这里先讨论非公平锁,因为默认是非公平锁
lock()
final void lock() {
// 这里就是CAS去想替换锁状态state为1,如果成功就会把独占锁的线程设置为当前线程
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 替换失败,也就是竞争锁失败,调用acquire()方法
acquire(1);
}
acquire()
public final void acquire(int arg) {
// 尝试获取锁,tryAcquire
if (!tryAcquire(arg) &&
// 这里是尝试获取锁失败了,那就用addWaiter方法把当前线程加入进同步队列
// acquireQueued就是在队列中自旋尝试获取锁
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// 这里acquireQueued返回的就是中断标记,如果获取锁失败并且是被中断了,这里就自我中断一下,把中断标记为设置一下。
selfInterrupt();
}
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 看一下当前线程的状态
int c = getState();
// 如果为0可以直接尝试获取
if (c == 0) {
// 如果CAS成功就成功获取锁
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 本身已经获得锁了
else if (current == getExclusiveOwnerThread()) {
// 因为是可重入的,所以这里把锁的请求加上
int nextc = c + acquires;
// 这里是爆int的意思
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 这里不用CAS是因为本身线程已经有锁了
setState(nextc);
return true;
}
return false;
}
// 添加进入队列
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.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
// 这是在等待队列里尝试获取锁
final boolean acquireQueued(final Node node, int arg) {
// 如果非正常状态退出,最后finally会cancel掉点前节点以及之前设置为cancel的节点
boolean failed = true;
try {
// interrupt标志在parkAndCheckInterrupt会判断是否被中断
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;
}
// 返回失败的时候判断之前的节点是否为signal状态,signal状态说明前面节点都在等待中,那么此节点也肯定要等待,就随之把它park挂起。
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 获取前驱节点的状态
int ws = pred.waitStatus;
// 如果是SIGNAL说明前驱节点也在等待signal,那么就说明这个节点肯定也要等待,所以挂起
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
// 循环将前驱的为cancel的全部给删掉
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
// 如果前驱节点为其他的状态,那么就设置为SIGNAL状态
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
公平锁
公平锁的区别主要就是在第一次tryAcquire的时候要判断hasQueuedPredecessors,其实也就是判断是否有在head之后的节点在等着,并且还不是当前的节点
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
Node的状态
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
static final Node EXCLUSIVE = null;
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;
在上面的源码中基本上就把ReentrantLock的lock方法获得锁的模式给解释清楚了。
ReentrantLock还提供了其他方法比如:
- tryLock:这个方法其实就是调用了之前说的tryLock,就只是进行一次获取锁,获取失败就返回
- tryLock(timeout, unit) //这是一个超时方法,它会对中断敏感,它所做的就是在超时时间内获取锁
- lockInterruptibly() // 这也是支持中断的加锁方法,它和上面这个方法的区别就在于,上面的支持超时也支持中断,而这个不行
- isHeldByCurrentThread() 返回当前线程是否获得了锁
从源码上理解ReentrantLock释放锁
unlock()
release(int arg)
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) {
// 把release的请求减去
int c = getState() - releases;
// 如果当前的线程不是拥有锁的线程,出大问题,抛异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
// 看是否成功释放的标志
boolean free = false;
// 如果全部释放完了,那才是成功释放
if (c == 0) {
free = true;
// 把锁的拥有者设置为null
setExclusiveOwnerThread(null);
}
// 重新设置一下c,这里也是因为有控制权所以不用CAS
setState(c);
// 返回是否释放了锁的所有权
return free;
}
// 释放后继
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
// 看下当前节点的状态
int ws = node.waitStatus;
// 把节点状态清空
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
// 从后往前遍历,找到下一个状态为SIGNAL的节点
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);
}