AQS解析(以ReentrantLock公平锁为例)

AQS解析以ReentrantLock公平锁为例

AQS中进程节点的状态

线程的2种等待模式:

  • SHARED:表示线程以共享的模式等待锁(如ReadLock)
  • EXCLUSIVE:表示线程以互斥的模式等待锁(如ReentrantLock),互斥就是一把锁只能由一个线程持有,不能同时存在多个线程使用同一个锁

线程在队列中的状态枚举:

  • CANCELLED:值为1,表示线程的获锁请求已经“取消”
  • SIGNAL:值为-1,表示该线程一切都准备好了,就等待锁空闲出来给我(比如现在头节点的值为-1,那么说明头节点的下一个节点的线程正在临界区,当其退出临界区的时候会释放锁,此时这个线程成为头节点,并查看自己的节点值是否为-1,如果为-1,说明之后有线程等待(线程插入的时候修改前面一个节点的值为-1),那么唤醒这个线程)。
  • CONDITION:值为-2,表示线程等待某一个条件(Condition)被满足
  • PROPAGATE:值为-3,当线程处在“SHARED”模式时,该字段才会被使用上(在后续讲共享锁的时候再细聊)

初始化Node对象时,默认为0

成员变量:

  • waitStatus:该int变量表示线程在队列中的状态,其值就是上述提到的CANCELLED、SIGNAL、CONDITION、PROPAGATE
  • prev:该变量类型为Node对象,表示该节点的前一个Node节点(前驱)
  • next:该变量类型为Node对象,表示该节点的后一个Node节点(后继)
  • thread:该变量类型为Thread对象,表示该节点的代表的线程
  • nextWaiter:该变量类型为Node对象,表示等待condition条件的Node节点(暂时不用管它,不影响我们理解主要知识点)
调用lock();
ReentrantLock lock = new ReentrantLock(true);
lock.lock();

会到

static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    final void lock() {
        acquire(1);
    }
}

Sync类继承于AbstractQueuedSynchronizer

AQS中的acquire方法,其中arg传的参数是说申请的锁里面修改state的数量为1,对于排他锁来说,一次只能申请一个,而共享锁可以多次进入,所以state的数量可以大于1

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

会先执行tryAcquire方法来尝试是否可以获得锁

protected final boolean tryAcquire(int acquires) {
  final Thread current = Thread.currentThread();
  //获得当前锁的状态,如果是排他锁且没有线程拿到锁,那么为0,如果有线程申请上了锁,那么为大于1
  int c = getState();
  //锁空闲
  if (c == 0) {
    //hasQueuedPredecessors()方法非公平锁没有,是去判断是在此线程之前是否有其他线程在等待这个锁,如果有,那么线程会排队。如果没有这个方法的判断就是非公平锁,前一个线程释放锁之后通知后一个线程的时候,后一个线程还没有拿到锁,此时突然有另外一个B线程出来申请锁,那么可能锁会被这个B线程申请上。
    //compareAndSetState(0, acquires)这个就是在申请锁,如果申请上了,那么会设置下一步setExclusiveOwnerThread(current),将当前拿到锁的线程保存一下。
    if (!hasQueuedPredecessors() &&
        compareAndSetState(0, acquires)) {
      setExclusiveOwnerThread(current);
      return true;
    }
  }
  //如果是可重入锁的话,state是可以大于0的,所以判断一个是不是拿到锁的线程是自己
  else if (current == getExclusiveOwnerThread()) {
    int nextc = c + acquires;
    if (nextc < 0)
      throw new Error("Maximum lock count exceeded");
    //直接修改state就可以了
    setState(nextc);
    return true;
  }
  //要不然的话申请失败
  return false;
}
}

如果tryAcquire尝试没有获得到锁,会执行acquireQueued(addWaiter(Node.EXCLUSIVE), arg)),先看addWaiter(Node.EXCLUSIVE),其主要是生成本线程的node,并将其放到链表的末尾,进行排队。

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) {
    //注意,先设置到末尾,这个对应于之后的unlock方法里的唤醒下一个排队线程时要从尾到头,因为下面的cas操作之后才会设置pred.next = node,不是一个原子的操作。
    node.prev = pred;
    //这里也是尝试设置一下,如果成功就非常好,直接设置就行
    if (compareAndSetTail(pred, node)) {
      pred.next = node;
      return node;
    }
  }
  //如果cas不成功,说明有其他线程的node也插入了,在enq中执行循环插入,直到成功为止。
  //或者说,  Node pred = tail;if (pred != null)这里,压根就没有链表,要新建立链表。
  //注意这里的新建链表是新建一个空的头,如何把node指向后
  enq(node);
  return node;
}

private Node enq(final Node node) {
  for (;;) {
    Node t = tail;
    //新建立链表
    if (t == null) { // Must initialize
      if (compareAndSetHead(new Node()))
        tail = head;
    } else {
      //疯狂试探插入
      node.prev = t;
      if (compareAndSetTail(t, node)) {
        t.next = node;
        return t;
      }
    }
  }
}

addWaiter执行完成后,会执行acquireQueued,其主要就是申请锁

final boolean acquireQueued(final Node node, int arg) {
  boolean failed = true;
  try {
    boolean interrupted = false;
    for (;;) {
      final Node p = node.predecessor();
      //只有前驱是头的时候(前驱拿到了锁,释放没有不清楚),才能尝试获得锁
      if (p == head && tryAcquire(arg)) {
        //如果拿到了锁,就把自己的节点当头
        setHead(node);
        p.next = null; // help GC
        failed = false;
        //成功拿到锁,返回interrupted,这之中如果设置了中断,之后也是要抛出的。
        return interrupted;
      }
      //如果再次(第二次)尝试还是没有拿到锁或者根本前驱也没有拿到锁,那么会先进入这里shouldParkAfterFailedAcquire(p, node),如果返回true,执行parkAndCheckInterrupt()
      if (shouldParkAfterFailedAcquire(p, node) &&
          parkAndCheckInterrupt())
        interrupted = true;
    }
  } finally {
    if (failed)
      cancelAcquire(node);
  }
}

shouldParkAfterFailedAcquire(p, node)中有

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
  //拿到前驱的waitStatus,默认是0
  int ws = pred.waitStatus;
  //如果是第二次进入shouldParkAfterFailedAcquire的时候,说明还是没有拿到锁,因为已经将前驱节点的waitStatus设置成了Node.SIGNAL,那么会直接返回true,执行parkAndCheckInterrupt()
  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.
             */
    //第一次到这里的时候会给前驱设置Node.SIGNAL,也就是-1,
    compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
  }
  return false;
}

parkAndCheckInterrupt()中有:

private final boolean parkAndCheckInterrupt() {
  //  LockSupport.park(this);直接就是把这个线程阻塞了,如下
  LockSupport.park(this);
  // 之后如果唤醒了的话(node里存有thread,因为调用UNSAFE唤醒),会返回thread的interrupt这个属性,因为在阻塞的时候,有可能其他线程会把这个阻塞的线程的interrupt属性改变,那么就要之后抛出中断。
  return Thread.interrupted();
}

//LockSupport
public static void park(Object blocker) {
  Thread t = Thread.currentThread();
  setBlocker(t, blocker);
  UNSAFE.park(false, 0L);
  setBlocker(t, null);
}

如果一直没有拿到,那么线程就阻塞了。

调用unlock()

当一个线程调用了unlock()之后,不仅要处理是否重入,还要唤醒之后链表中的线程。

public void unlock() {
  sync.release(1);
}

又是到了AQS里

public final boolean release(int arg) {
  if (tryRelease(arg)) {
    //到这里,说明没有重入的锁了,现在开始通知之后链表的线程
    Node h = head;
    //如果之后链表有线程,且已经阻塞的话,会给其链表前一个线程的node的waitStatus设置成-1,此时才执行unparkSuccessor通知被阻塞的线程,不然的话链表后的线程就是活跃的,不需要唤醒。
    if (h != null && h.waitStatus != 0)
      unparkSuccessor(h);
    return true;
  }
  return false;
}

先会执行tryRelease(arg)

protected final boolean tryRelease(int releases) {
  int c = getState() - releases;
  if (Thread.currentThread() != getExclusiveOwnerThread())
    throw new IllegalMonitorStateException();
  boolean free = false;
  //如果为0的话,说明没有重入了,可以释放了
  if (c == 0) {
    free = true;
    //将保存拿到锁的线程变量置空
    setExclusiveOwnerThread(null);
  }
  setState(c);
  return free;
}

protected final void setExclusiveOwnerThread(Thread thread) {
  exclusiveOwnerThread = thread;
}

执行unparkSuccessor(h)

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;
  //这里要把头节点的waitStatus设置成0,为什么?
  //因为唤醒之后,如果是非公平锁,如果有其他线程申请锁,那么也是可以拿到锁的,此时这一瞬间要这个唤醒的线程和那个其他线程进行竞争,如果竞争失败的话,那么这个唤醒的线程还需要再次执行之前的操作,所以他的前驱节点最好还是置成默认值0。
  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;
  //找后继节点,如果后继节点不存在(有可能是前面addWaiter设置后继节点的代码还没执行),或者waitStatus大于0(后继的线程获取lock取消,比如超时了),那么就需要再找后继的后继,直到找到第一个结束。
  if (s == null || s.waitStatus > 0) {
    s = null;
    //从后往前找,因为前面addWaiter中是先设置后继指向前驱的,再cas,再前驱指向后继,所以cas成功设置的时候,一定有后继指向前驱设置好了。addWaiter先这里的节点连接操作并不是原子,也就是说在多线程并发的情况下,可能会出 现个别节点并没有设置 next 值,就失败了,所以从尾节点逆向遍历。
    for (Node t = tail; t != null && t != node; t = t.prev)
      if (t.waitStatus <= 0)
        s = t;
  }
  if (s != null)
    //找到后,唤醒。
    LockSupport.unpark(s.thread);
}

Condition

当一个condition调用的时候,会把进程阻塞,还是叫挂起

ReentrantLock lock = new ReentrantLock(true);
Condition cond = lock.newCondition();

new Thread(new Runnable() {
  @Override
  public void run() {
    lock.lock();
    try {
      cond.await();
      System.out.println(1);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    lock.unlock();
  }
}).start();

await()方法

因为在调用condition的时候必拿到lock的锁,而调用了await()方法后线程会被挂起,执行不到lock.unlock(),那么需要在await()方法里把锁去除(state置0),并且通知后续的线程(把head node的next node unpark)

public final void await() throws InterruptedException {
  if (Thread.interrupted())
    throw new InterruptedException();
  // 当前线程加入Conditon等待队列
  Node node = addConditionWaiter();
  // 释放同步状态,即释放同步锁;唤醒当前线程的节点(头节点)的后继节点,因为调用condition.await()之后这个线程就会被阻塞,所以要通知后续阻塞的节点(是通知的锁那个链条上的节点)。
  int savedState = fullyRelease(node);
  int interruptMode = 0;
  // 如果节点不在同步队列中,这个创建的node是放在condition当中的,不是放在lock的链表中的
  // 之后唤醒的时候就在lock的链表中了,就可以退出了。
  while (!isOnSyncQueue(node)) {
    // 阻塞当前线程
    LockSupport.park(this);
    // 如果中断被唤醒,循环停止 
    // 判断此次线程的唤醒是否因为线程被中断, 
    // 若是被中断, 则会在checkInterruptWhileWaiting的transferAfterCancelledWait进行节点的转移; 
    // 返回值interruptMode != 0
    if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
      break;
  }
  // 调用 acquireQueued在Sync Queue里面进行独占锁的获取, 返回值表明在获取的过程中有没有被中断过
  if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
    interruptMode = REINTERRUPT;
   // 判断线程的唤醒是中断还是signal
   // 如通过中断唤醒的话, 此刻代表线程的Node在Condition Queue与Sync Queue里面都会存在
  if (node.nextWaiter != null) // clean up if cancelled
    unlinkCancelledWaiters();
  // 通过中断的方式唤醒线程
  if (interruptMode != 0)
    // 根据 interruptMode 的类型决定是抛出异常, 还是自己中断一下
    reportInterruptAfterWait(interruptMode);
}

这个LockSupport.park(this);中的this,指的是condtion这个实例,所以将这个线程和这个condtion绑定在了一起。

addConditionWaiter()方法比较简单,因为调用condtion的await()方法的时候已经拿到了锁,所以必须是一个一个的进的,所以不需要额外的cas

private Node addConditionWaiter() {
    Node t = lastWaiter;
    // If lastWaiter is cancelled, clean out.
    if (t != null && t.waitStatus != Node.CONDITION) {
        unlinkCancelledWaiters();
        t = lastWaiter;
    }
  //注意,虽然生成的node和lock中的node一样,但是这个node是放在condition中的,也为了区分,设置waitstatues为Node.CONDITION,-2
    Node node = new Node(Thread.currentThread(), Node.CONDITION);
    if (t == null)
        firstWaiter = node;
    else
        t.nextWaiter = node;
    lastWaiter = node;
    return node;
}

fullyRelease方法也比较简单

final int fullyRelease(Node node) {
  boolean failed = true;
  try {
    int savedState = getState();
    if (release(savedState)) {
      failed = false;
      return savedState;
    } else {
      throw new IllegalMonitorStateException();
    }
  } finally {
    if (failed)
      node.waitStatus = Node.CANCELLED;
  }
}
public final boolean release(int arg) {
  if (tryRelease(arg)) {
    Node h = head;
    if (h != null && h.waitStatus != 0)
      unparkSuccessor(h);
    return true;
  }
  return false;
}

再来看看condition.signalAll方法

使用:

new Thread(new Runnable() {
  @Override
  public void run() {
    lock.lock();
    cond.signalAll();
    System.out.println(3);
    lock.unlock();
  }
}).start();
public final void signalAll() {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
        doSignalAll(first);
}

isHeldExclusively是判断当前线程是否已经拿到了锁

protected final boolean isHeldExclusively() {
  // While we must in general read state before owner,
  // we don't need to do so to check if current thread is owner
  return getExclusiveOwnerThread() == Thread.currentThread();
}
//拿到锁之后,会将exclusiveOwnerThread属性设置为拿到锁的线程
protected final Thread getExclusiveOwnerThread() {
  return exclusiveOwnerThread;
}

doSignalAll(first)方法

private void doSignalAll(Node first) {
  lastWaiter = firstWaiter = null;
  do {
    Node next = first.nextWaiter;
    first.nextWaiter = null;
    transferForSignal(first);
    first = next;
  } while (first != null);
}
//对比一下signal方法,只是拿到第一个node,并确保加入了lock的链表中
private void doSignal(Node first) {
  do {
    if ( (firstWaiter = first.nextWaiter) == null)
      lastWaiter = null;
    first.nextWaiter = null;
  } while (!transferForSignal(first) &&
           (first = firstWaiter) != null);
}

transferForSignal方法

final boolean transferForSignal(Node node) {
  /*
         * If cannot change waitStatus, the node has been cancelled.
         */
  //确保这个node一开始是 Node.CONDITION类型的,之后要加入lock中,所以设置为0
  if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
    return false;

  /*
         * Splice onto queue and try to set waitStatus of predecessor to
         * indicate that thread is (probably) waiting. If cancelled or
         * attempt to set waitStatus fails, wake up to resync (in which
         * case the waitStatus can be transiently and harmlessly wrong).
         */
  //将node加入lock,返回的是加入之后node的前驱
  Node p = enq(node);
  int ws = p.waitStatus;
  //修改前驱的waitStatus为Node.SIGNAL,如果设置不成功的话,直接唤醒node
  if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
    LockSupport.unpark(node.thread);
  return true;
}

doSignalAll将condition的node全部加入lock的链表中,调用完成后,会有lock.unlock(),之后就是走流程唤醒了。

而在调用唤醒后,线程会在await方法中,跳出while (!isOnSyncQueue(node))循环,执行接下来的任务:

但是首先会判断一下,是否是中断

// 如果中断被唤醒,循环停止 
// 判断此次线程的唤醒是否因为线程被中断, 
// 若是被中断, 则会在checkInterruptWhileWaiting的transferAfterCancelledWait进行节点的转移; 
// 返回值interruptMode != 0
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
	break;

private int checkInterruptWhileWaiting(Node node) {
  return Thread.interrupted() ?
    (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
  0;
}
//如果是中断唤醒,将node的放进lock的链表中
final boolean transferAfterCancelledWait(Node node) {
  if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
    enq(node);
    return true;
  }
  /*
         * If we lost out to a signal(), then we can't proceed
         * until it finishes its enq().  Cancelling during an
         * incomplete transfer is both rare and transient, so just
         * spin.
         */
  //如果上面的没有完成,说明有可能其他进程修改了node,那么调用Thread.yield()将线程变为就绪状态,直到其他线程把其放入lock的链表中
  while (!isOnSyncQueue(node))
    Thread.yield();
  return false;
}
// 调用 acquireQueued在Sync Queue里面进行独占锁的获取, 返回值表明在获取的过程中有没有被中断过
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
  interruptMode = REINTERRUPT;
// 判断线程的唤醒是中断还是signal
// 如通过中断唤醒的话, 此刻代表线程的Node在Condition Queue与Sync Queue里面都会存在,此时将Condition Queue的清理一下
if (node.nextWaiter != null) // clean up if cancelled
  unlinkCancelledWaiters();
// 通过中断的方式唤醒线程
if (interruptMode != 0)
  // 根据 interruptMode 的类型决定是抛出异常, 还是自己中断一下
  reportInterruptAfterWait(interruptMode);

acquireQueued

final boolean acquireQueued(final Node node, int arg) {
  boolean failed = true;
  try {
    boolean interrupted = false;
    for (;;) {
      final Node p = node.predecessor();
      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);
  }
}

跟前面的acquireQueued是一个方法,拿到则ok,没拿到就挂起。

lockInterruptibly()和 interrupt()

ReentrantLock.lockInterruptibly允许在等待时(注意不是挂起)由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回,这时不用获取锁,而会抛出一个InterruptedException。 ReentrantLock.lock方法不允许Thread.interrupt中断,即使检测到Thread.isInterrupted,一样会继续尝试获取锁,失败则继续休眠。只是在最后获取锁成功后再把当前线程置为interrupted状态,然后再中断线程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值