关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

JUC-Lock

发布时间:2023-06-26 14:00:12

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);  }

/template/Home/leiyu/PC/Static