写的有点乱,因为无论是ReadWriteLock还是ReentrantLock加锁解锁过程本身就很乱。
只写了lock方法,lockInterruptibly方法其实和lock差不多,只是再睡眠的时候被打断了醒来后发现被打断了就抛出了一个异常,其余没什么差别。
首先读写锁也是有公平和非公平之分。这一点要注意。默认是非公平锁。
加锁
public void lock() {
sync.acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire方法
protected final boolean tryAcquire(int acquires) {
/*
* Walkthrough:
* 1. If read count nonzero or write count nonzero
* and owner is a different thread, fail.
* 2. If count would saturate, fail. (This can only
* happen if count is already nonzero.)
* 3. Otherwise, this thread is eligible for lock if
* it is either a reentrant acquire or
* queue policy allows it. If so, update state
* and set owner.
*/
//当前线程
Thread current = Thread.currentThread();
//是否上锁
int c = getState();
int w = exclusiveCount(c);
//因为写锁是互斥锁,只要有读锁或者写锁都不能上锁成功
//除非重入,c!=0表示已经有人上锁了
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
// 如果w == 0 表示没有人加写锁,此时是读锁,只有
// 重入可以枷锁成功。
//不过我不推荐读写锁读锁升级为写锁,会导致死锁。
//因为互斥,所以只要线程不相同不能重入,就返回false
if (w == 0 || current != getExclusiveOwnerThread())
return false;
//重入的情况,大于重入的最大值直接溢出报错。
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
//直接上锁,不需要cas,因为这个是重入写锁加写锁情况。
setState(c + acquires);
return true;
}
//writeShouldBlock方法在下面有介绍
//当他不需要排队,或者说公平锁情况下,会直接进行cas操作,将c也就是state由0改为1
//如果加锁失败,整个方法返回false;
//这个地方主要是当他发现当前无人持有锁,得去看有没有
//其他线程也在等
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
//如果加锁成功,将线程设到AbstractOwnableSynchronizer这个里面去
setExclusiveOwnerThread(current);
return true;
}
exclusiveCount方法
static final int SHARED_SHIFT = 16;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
//相当于c与 16个1。这个地方计算的是当前写锁的情况
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
writerShouldBlock 方法
//公平锁,判断自己需不需要排队
final boolean writerShouldBlock() {
//这个方法我之后如果有时间写ReentrantLock的时候再详细说吧,
//他其实是aqs里面很经典很重要的一个方法,就是判断自己需不需要排队
return hasQueuedPredecessors();
}
//非公平锁,直接抢资源就完事了
final boolean writerShouldBlock() {
return false; // writers can always barge
}
addWaiter方法
aqs经典方法
private Node addWaiter(Node mode) {
//new 出一个node,
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
//拿到aqs的尾节点。
Node pred = tail;
//如果尾节点不为null,说明aqs已经被初始化
if (pred != null) {
//就是节点指向,链表维护。
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//如果尾节点为null,或者cas失败(表示同一个时间点,多个线程cas执行,只成功一个)
//往下看enq方法
enq(node);
return node;
}
enq方法
aqs经典方法
private Node enq(final Node node) {
for (;;) {
//拿到尾节点
Node t = tail;
//如果为空,则说明队列未初始化
if (t == null) { // Must initialize
//初始化队列。
if (compareAndSetHead(new Node()))
tail = head;
} else {
//这个和上一个的入队操作一样,注意这个地方是死循环,哪怕几千个
//线程同时cas去抢资源,无线循环也会入队完成。
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
acquireQueued 方法
之前这个node已经入队了。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//拿到node的前一个节点
final Node p = node.predecessor();
//如果上一个节点是aqs的头节点,再尝试加锁一次,避免park,真牛这代码
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//第一次shouldParkAfterFailedAcquire为false,继续来一次。
//第二次p.waitstatus变成-1,则准备park了。
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
//解锁成功后,不管是不是false还是true都进入下一轮循环。
//去尝试加锁,去竞争。
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire方法
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//拿到前一个节点waitStatus默认是0
int ws = pred.waitStatus;
//第二次就是-1.返回true
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.
*/
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.
*/
//将他改成-1退出为false
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
parkAndCheckInterrupt方法
private final boolean parkAndCheckInterrupt() {
//自旋
LockSupport.park(this);
//这个其实再这个地方没什么卵子用,lockInterruptibly()这个方法的时候,后面直接抛异常
//dog李这里就复用了这一段代码。可读性较差(唯一能蔑视大神的点了哈哈哈)
//解锁成功后直接返回
return Thread.interrupted();
}
解锁
public void unlock() {
sync.release(1);
}
release方法
aqs的方法之一
public final boolean release(int arg) {
//把state置为0之后
if (tryRelease(arg)) {
//拿到头节点
Node h = head;
//如果头节点的waitStatus != 0 就unpark他下一个节点。
//这里注意,waitStatus 初始化为0,如果不为0,说明
//有第二个线程把他置为-1 或者什么什么的,说明有人排队
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
tryRelease方法
protected final boolean tryRelease(int releases) {
//解锁的线程不是当前的线程。抛异常。
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//把之前的写锁的后16位去-1.
int nextc = getState() - releases;
//如果这个时候是0,表示没人加锁。
boolean free = exclusiveCount(nextc) == 0;
//把持有锁的线程置位null
if (free)
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}
unparkSuccessor 方法
aqs方法之一
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.
*/
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
//这里我也不清楚为什么循环,可能他只是想waitStatus<0的节点。
//我猜测之前waitStatus再某个地方某个逻辑(可能是取消的时候)设置成>0了。
//果然顺着源码去读,验证猜测。
/** waitStatus value to indicate thread has cancelled
static final int CANCELLED = 1;*/
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// s != null 就去唤醒他
if (s != null)
LockSupport.unpark(s.thread);
}