ReentrantLock
ReentrantLock : 可以创建 公平 or 非公平锁, 本问主要说非 公平锁
知,lock, trylock,lockInterruptibly
这里是架构图,太简单了 不补了
ReentrantLock 内部主要类
/** * 同步控制的基类.下边有两个子类:非公平和公平 *.使用了AbstractQueuedSynchronizer类的的一些方法 */ static abstract class Sync extends AbstractQueuedSynchronizer /** * 非公平 */ final static class NonfairSync extends Sync /** * 公平 */ final static class FairSync extends Sync
2. 非公平锁的lock 过程
final void lock() { if (compareAndSetState(0, 1)) // 我 插下队, 现在要是没有在用我就直接试一试 setExclusiveOwnerThread(Thread.currentThread()); // 当前线程OK else acquire(1); //去得到锁 表示出阻塞了 }
public final void acquire(int arg) { if (!tryAcquire(arg) && // 尝试的去获取下 acquireQueued(addWaiter(Node.EXCLUSIVE), arg))// 生成Node入队后在轮询等到他可以执行, 如果在这个过程中被中断那么, 当这个线程获得锁的时候出来后会返回true selfInterrupt();// 中断了我擦 }
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);// 插队成功yes 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; }
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;// OK suc return node; } } enq(node);// 没办法入队 失败了,慢慢来 return node; } private Node enq(final Node node) { for (;;) {
Node t = tail; if (t == null) { // 没有元素啊,那我初始化下 if (compareAndSetHead(new Node()))// cas替换下 tail = head; //OK 有头了我 重来下 } else { // 和一开始的入队 逻辑一样的
node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } } final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false;
// 循环直到 本NODE成为 head成为可执行方式, 没被挂起前是自旋锁 for (;;) { final Node p = node.predecessor();
// 前一个节点是 head啊 那么 来吧我们尝试 着成为 拥有者, 因为存在插队情况 所以 会有try的字样。
// 值得 一提的 是 如果 我被 unpark但是 又被别人抢了, 那我 就 会 自旋,在挂起 if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && // 捕获失败了是不是要挂起啊
parkAndCheckInterrupt()) // 挂起 成为重(重量的重)锁, 阻塞等待 unpark。并且检测 是否被中断? interrupted = true; // 标记 } } finally { if (failed)// 失败 不可能的啊 cancelAcquire(node); } }
- CANCELLED,值为1,因为超时或者中断,结点会被设置为取消状态,被取消状态的结点不应该去竞争锁,只能保持取消状态不变,不能转换为其他状态。处于这种状态的结点会被踢出队列,被GC回收;
- SIGNAL,值为-1,表示这个结点的继任结点被阻塞了,到时需要通知(unpark)
- CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中
- PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执,使用在共享模式头结点有可能牌处于这种状态,表示锁的下一次获取可以无条件传播;
- 值为0,表示当前节点在sync队列中,等待着获取锁。新结点会处于这种状态。(获取不到缩就会被转换成 SIGNAL)
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; 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. */
// 从ws -> signal
compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; } private final boolean parkAndCheckInterrupt() { LockSupport.park(this);// 阻塞这个线程 unpark函数可以先于park调用,比如线程A调用unpark函数,给线程B发了一个“许可”,那么当线程B调用park时,它发现已经有“许可”了,那么它会马上再继续运行 而这是 wait notify 所做不到的 return Thread.interrupted(); // 清除中断标记, 并且返回 刚才被中断了么 }
公平锁和 非公平的差不多, 只不过没有插队的 逻辑, 所以就不说了
3. unlock过程
public void unlock() { sync.release(1); } public final boolean release(int arg) { if (tryRelease(arg)) { // 尝试 释放 Node h = head; // 释放成功, 那么获取头部 if (h != null && h.waitStatus != 0) // 存在 头节点 且 不是取消的哦状态 unparkSuccessor(h); // 对 距离最近的 节点做唤醒操作。 return true; // suc } return false; // what? }
protected final boolean tryRelease(int releases) { // 释放 int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) // 当前县城 没有锁啊 , 没有lock 就 unlock了 throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { // 锁都释放了我去 free = true; setExclusiveOwnerThread(null); // 再见 }
// 存在 重入 d setState(c);// 设置状态 return free; }
- CANCELLED,值为1,因为超时或者中断,结点会被设置为取消状态,被取消状态的结点不应该去竞争锁,只能保持取消状态不变,不能转换为其他状态。处于这种状态的结点会被踢出队列,被GC回收;
- SIGNAL,值为-1,表示这个结点的继任结点被阻塞了,到时需要通知(unpark)
- CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中
- PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执,使用在共享模式头结点有可能牌处于这种状态,表示锁的下一次获取可以无条件传播;
- 值为0,表示当前节点在sync队列中,等待着获取锁。新结点会处于这种状态。(获取不到缩就会被转换成 SIGNAL)
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; // head的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); // 唤醒 }
总结 : 我被 unpark但是 又被别人抢了, 那我 就 会 自旋,在挂起 , 所以无论怎么讲这是 多个线程表演的舞台,他们各尽其能去获取, 是通过CAS 控制的 保证的。 有队列,有插队。
啊四大四大四大