JUC-Lock

ReentrantLock
1.ReentrantLock分为两种实现,非公平以及公平模式;
2.公平模式基于CAS+队列实现,而非公平模式则是仅基于CAS实现
3.ReentrantLock中的队列,本质上是基于AQS实现的;而AQS本质上只是一个维持了内存可见的state的双向队列
//代表当前线程取消竞争锁 static final int CANCELLED = 1; //代表当前节点正常 static final int SIGNAL = -1; //代表当前线程在等待条件 static final int CONDITION = -2; //无法理解....... static final int PROPAGATE = -3; //表示状态字;只能用于以上四个状态字以及0 volatile int waitStatus;
//默认为非公平实现 public ReentrantLock() {  sync = new NonfairSync();  } //通过传入fair变量控制; //fair=true,公平锁实现;否则非公平实现  public ReentrantLock(boolean fair) {  sync = fair ? new FairSync() : new NonfairSync();  }

final void lock() {  acquire(1);  }
public final void acquire(int arg) {  //至少尝试获取一次锁;  //如果锁没有获取成功,则将当前线程加入等待队列;  //并且中断当前线程  if (!tryAcquire(arg) &&  acquireQueued(addWaiter(Node.EXCLUSIVE), arg))  selfInterrupt();  } • ReentrantLock#tryAcquire(int args)
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;  }
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;  }
private final boolean parkAndCheckInterrupt() {  //挂起当前线程  LockSupport.park(this);  return Thread.interrupted();  }
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) {  //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;  }
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);  }						Copyright © 2023 leiyu.cn. All Rights Reserved. 磊宇云计算 版权所有 许可证编号:B1-20233142/B2-20230630 山东磊宇云计算有限公司 鲁ICP备2020045424号
磊宇云计算致力于以最 “绿色节能” 的方式,让每一位上云的客户成为全球绿色节能和降低碳排放的贡献者