JUC-Lock
- Lock接口:
- Lock接口定义了比同步原语更灵活、更轻巧锁机制;同时,支持多种条件关联锁
- JUC的Lock接口的基本实现为ReentrantLock
- ReentrantLock支持公平、非公平、可重入的互斥同步锁
ReentrantLock
1.ReentrantLock分为两种实现,非公平以及公平模式;
2.公平模式基于CAS+队列实现,而非公平模式则是仅基于CAS实现
3.ReentrantLock中的队列,本质上是基于AQS实现的;而AQS本质上只是一个维持了内存可见的state的双向队列
- ReentrantLock中Node的状态字
//代表当前线程取消竞争锁 static final int CANCELLED = 1; //代表当前节点正常 static final int SIGNAL = -1; //代表当前线程在等待条件 static final int CONDITION = -2; //无法理解....... static final int PROPAGATE = -3; //表示状态字;只能用于以上四个状态字以及0 volatile int waitStatus;
- ReentrantLock构造方法
//默认为非公平实现 public ReentrantLock() { sync = new NonfairSync(); } //通过传入fair变量控制; //fair=true,公平锁实现;否则非公平实现 public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
- sync是什么?
- Sync是基于AQS实现的ReentrantLock锁同步控制基础
- 如下:
- ReentrantLock#lock()方法
final void lock() { acquire(1); }
- ReentrantLock中acquire方法基于AQS
public final void acquire(int arg) { //至少尝试获取一次锁; //如果锁没有获取成功,则将当前线程加入等待队列; //并且中断当前线程 if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } • ReentrantLock#tryAcquire(int args)
- 尝试获取锁的公平版本
- 非公平版本其实就是略去了公平版本的队列判断:hasQueuedPredecessors()
protected final boolean tryAcquire(int acquires) { //当前线程ID final Thread current = Thread.currentThread(); //当前锁状态 int c = getState(); //如果当前锁无人占有 if (c == 0) { //公平锁讲究先来后到 //如果当前等待队列没有等待线程, //且通过CAS成功改变锁的状态 if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { //设置当前锁持有的线程 setExclusiveOwnerThread(current); return true; } } //如当前事务请求线程与当前事务执行线程是一样的;可重入锁特性 //这里就是可重入锁特性:同一线程可以重复获取同一把锁 else if (current == getExclusiveOwnerThread()) { //那么将当前锁持有状态+1 //通过设置acquires=1来完成可重入锁特性 int nextc = c + acquires; //这里对可重入锁的限制,当同一线程持有锁达到int的上限时; //会抛出异常 if (nextc < 0) throw new Error("Maximum lock count exceeded"); //并且重新改变state setState(nextc); return true; } return false; } • ReentrantLock#addWaiter() • 将当前等待线程以排它模式入队 private Node addWaiter(Node mode) { //以当前线程建立Node,并且设置Node的模式为排它 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; //通过CAS修改队尾指针,新增等待线程 if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } //通过自旋追加节点 enq(node); return node; }
- ReentrantLock#acquireQueued()
- 通过自旋不停的获取锁
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { //获取p的前置节点 final Node p = node.predecessor(); //如果p的前置节点为head哑节点 //则说明p节点为阻塞队列的对头元素 //在AQS的CLH队列中,只有队头元素才能抢占锁 //且尝试获取锁成功,则返回false //当返回false时,则说明当前线程不需要挂起 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); } } • ReentrantLock#shouldParkAfterFailedAcquire() • 检验当前线程是否需要挂起 • 每个线程第一次进入该方法时,都为false;当第二次进入时才为true
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { //前驱节点状态字 int ws = pred.waitStatus; //前驱节点正常;我需要挂起 if (ws == Node.SIGNAL)//-1 return true; //当状态字大于0,说明前驱节点取消竞争锁; //此时不停的寻找状态字小于0的节点; //因为前驱节点的状态字影响了自身的行为; //自身线程的行为依赖于前驱节点的状态字 if (ws > 0) {//1 do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { //当一个新的节点进入队列时,默认status为0; //因此需要将前驱节点的status设置为-1; compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }
- ReentrantLock#parkAndCheckInterrupt()
- 挂起当前线程
private final boolean parkAndCheckInterrupt() { //挂起当前线程 LockSupport.park(this); return Thread.interrupted(); }
- ReentrantLock#release()
- 释放当前线程,唤醒等待线程
public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) //唤醒后继节点线程 unparkSuccessor(h); return true; } return false; }
- ReentrantLock#tryRelease()
- 尝试释放当前线程
protected final boolean tryRelease(int releases) { //state表示锁状态;亦是当前线程持有锁的数目 //可重入锁的释放 int c = getState() - releases; //如果当前线程不是锁的持有者 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; //如果说c==0;则说明当前线程持有的锁已经完全释放 //可重入锁,一个线程可以多次请求同一把锁 if (c == 0) { free = true; //设置当前锁的持有线程为null setExclusiveOwnerThread(null); } //修改状态位 setState(c); return free; }
- ReentrantLock#unparkSuccessor()
- 唤醒后继线程
private void unparkSuccessor(Node node) { //head节点的状态 int ws = node.waitStatus; if (ws < 0) //修改head节点的状态,以影响后继节点 compareAndSetWaitStatus(node, ws, 0); Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; //从后面往前面找,找最接近头节点的小于0的线程 for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } //如果存在状态位小于0的线程 if (s != null) //唤醒线程 LockSupport.unpark(s.thread); }