ReadWriteLock源码阅读之WriteLock加锁解锁过程源码分析

it2025-12-03  3

写的有点乱,因为无论是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); }
最新回复(0)