ReentrantLock使用和源码分析

基本介绍

相对于 synchronized 它具备如下特点

  1. 可中断
  2. 可以设置为公平锁
  3. 可以设置超时时间
  4. 支持多个条件变量,即对与不满足条件的线程可以放到不同的集合中等待

与 synchronized 一样,都支持可重入

基本语法

// 获取锁
reentrantLock.lock();
try {
 // 临界区
} finally {
 // 释放锁
 reentrantLock.unlock();
}

可重入

可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住

static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
 	method1();
}
public static void method1() {
     lock.lock();
     try {
     	log.debug("execute method1");
    	 method2();
     } finally {
     	lock.unlock();
     }
}
public static void method2() {
     lock.lock();
     try {
     	log.debug("execute method2");
     	method3();
     } finally {
    	 lock.unlock();
     }
}
public static void method3() {
     lock.lock();
         try {
         log.debug("execute method3");
     } finally {
     	lock.unlock();
     }
}

可打断

  • lock.lockInterruptibly();是可以打断的。

  • lock.lock()是不可以打断的。

lock.lockInterruptibly()底层会调用:

ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> { 
	log.debug("启动...");
     try {
     	lock.lockInterruptibly();
     } catch (InterruptedException e) {
         e.printStackTrace();
         log.debug("等锁的过程中被打断");
     	return;
     }
     try {
    	 log.debug("获得了锁");
     } finally {
    	 lock.unlock();
     }
}, "t1");
lock.lock();
log.debug("获得了锁");
t1.start();
try {
     sleep(1);
     t1.interrupt();
     log.debug("执行打断");
} finally {
 	lock.unlock();
}

输出:

18:02:40.520 [main] c.TestInterrupt - 获得了锁
18:02:40.524 [t1] c.TestInterrupt - 启动... 
18:02:41.530 [main] c.TestInterrupt - 执行打断
java.lang.InterruptedException 
 at 
java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchr
onizer.java:898) 
 at 
java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchron
izer.java:1222) 
 at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335) 
 at cn.itcast.n4.reentrant.TestInterrupt.lambda$main$0(TestInterrupt.java:17) 
 at java.lang.Thread.run(Thread.java:748) 
18:02:41.532 [t1] c.TestInterrupt - 等锁的过程中被打断

公平锁

synchronized锁中,在entrylist等待的锁在竞争时不是按照先到先得来获取锁的,所以说synchronized锁时不公平的;

ReentranLock锁默认是不公平的,但是可以通过设置实现公平锁。

ReentrantLock lock = new ReentrantLock(true);

本意是为了解决之前提到的饥饿问题,但是公平锁一般没有必要,会降低并发度,使用trylock更好

其实这两种实现的差别只有两点(后面的原理的获取锁中我们会讲到):

  1. 在lock()方法开始的时候非公平锁会尝试cas抢占锁插一次队;
  2. 在tryAcquire()方法发现state为0的时候,非公平锁会抢占一次锁,而公平锁会判断AQS链表中是否存在等待的线程,没有等待的线程才会去抢占锁。

锁超时

private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
        log.debug("尝试获得锁");
        try {
            lock.lockInterruptibly();
            //等待2s后没获取锁就超时了
            if (! lock.tryLock(2, TimeUnit.SECONDS)) {
                log.debug("获取不到锁");
                return;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            log.debug("获取不到锁");
            return;
        }
        try {
            log.debug("获得到锁");
        } finally {
            lock.unlock();
        }
    }, "t1");

    lock.lock();
    log.debug("获得到锁");
    t1.start();
    sleep(1);
    log.debug("释放了锁");
    lock.unlock();
}

条件变量

synchronized 中也有条件变量,就是我们讲原理时那个 waitSet 休息室,当条件不满足时进入 waitSet 等待
ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持多个条件变量的,这就好比

  1. synchronized 是那些不满足条件的线程都在一间休息室等消息
  2. 而 ReentrantLock 支持多间休息室,有专门等烟的休息室、专门等早餐的休息室、唤醒时也是按休息室来唤醒

使用要点:

  1. await 前需要获得锁
  2. await 执行后,会释放锁,进入 conditionObject 等待
  3. await 的线程被唤醒(或打断、或超时)取重新竞争 lock 锁,执行唤醒的线程爷必须先获得锁
  4. 竞争 lock 锁成功后,从 await 后继续执行
static final Object room = new Object();
static boolean hasCigarette = false;
static boolean hasTakeout = false;
static ReentrantLock ROOM = new ReentrantLock();
// 等待烟的休息室
static Condition waitCigaretteSet = ROOM.newCondition();
// 等外卖的休息室
static Condition waitTakeoutSet = ROOM.newCondition();

public static void main(String[] args) {
    new Thread(() -> {
        ROOM.lock();
        try {
            log.debug("有烟没?[{}]", hasCigarette);
            while (!hasCigarette) { 
                log.debug("没烟,先歇会!");
                try {
                    waitCigaretteSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.debug("可以开始干活了");
        } finally {
            ROOM.unlock();
        }
    }, "小南").start();

    new Thread(() -> {
        ROOM.lock();
        try {
            log.debug("外卖送到没?[{}]", hasTakeout);
            while (!hasTakeout) {
                log.debug("没外卖,先歇会!");
                try {
                    waitTakeoutSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.debug("可以开始干活了");
        } finally {
            ROOM.unlock();
        }
    }, "小女").start();

    sleep(1);
    new Thread(() -> {
        ROOM.lock();
        try {
            hasTakeout = true;
            waitTakeoutSet.signal();
        } finally {
            ROOM.unlock();
        }
    }, "送外卖的").start();

    sleep(1);

    new Thread(() -> {
        ROOM.lock();
        try {
            hasCigarette = true;
            waitCigaretteSet.signal();
        } finally {
            ROOM.unlock();
        }
    }, "送烟的").start();
}

原理

AQS
  • 基于先进先出FIFO实现的等待队列,AQS队列是由Node节点组成的双向链表实现的,所有的操作都是在这个AQS队列当中,如果一个线程获取锁就直接成功,如果失败了就将其放入等待队列当中。

  • 用 state 属性来表示资源的状态

    • getState - 获取 state 状态
    • setState - 设置 state 状态
    • compareAndSetState - cas 机制设置 state 状态
  • 每当有新线程请求同步状态时都会进入一个等待队列,等待队列通过双向链表实现,线程被封装在链表的 Node 节点中,Node 的等待状态包括:CANCELLED(1,取消),SIGNAL(-1,等待被唤醒),CONDITION(-2,线程正在等待),PROPAGATE(-3,后继节点会传播唤醒操作,只作用于共享模式),其中ReentrantLock涉及到的状态就是SIGNAL,CANCELLED。

  • 两种模式

    • 独占模式下锁只会被一个线程占用,其他线程必须等持有锁的线程释放锁后才能获取锁。(ReentrantLock)

      获取同步状态时,调用 acquire 方法的 tryAcquire 方法安全地获取同步状态,获取失败的线程会被构造同步节点并通过 addWaiter 方法加入到同步队列的尾部,在队列中自旋。之后调用 acquireQueued 方法使得节点以死循环的方式获取同步状态,如果获取不到则阻塞,被阻塞线程的唤醒依靠前驱节点的出队或中断。后继节点的线程被唤醒后需要检查自己的前驱节点是否是头节点,目的是维护同步队列的 FIFO 原则,节点之间在循环检查的过程中基本不通信,而是简单判断自己的前驱是否为头节点。

      释放同步状态时,同步器调用 tryRelease 方法释放同步状态,然后调用 unparkSuccessor 方法唤醒头节点的后继节点,使后继节点重新尝试获取同步状态。

    • 共享模式下多个线程可以获取同一个锁。(ReentrantReadWriteLocksemaphoer)

      获取同步状态时,调用 acquireShared 方法的 tryAcquireShared 方法,返回值为 int 类型,值不小于 0 表示能去获取锁。

      释放同步状态时,调用 releaseShared 方法,释放后会唤醒后续处于等待状态的节点。它和独占式的区别在于 tryReleaseShared 方法必须确保同步状态安全释放,通过循环 CAS 保证。

获取锁
  • CAS操作抢占锁,抢占成功则修改锁的状态为1,并设置锁的owner

    不公平:

    公平:

  • 抢占不成功

    • 先通过tryAcquire尝试获取锁:

      若锁状态为0,则CAS操作抢占锁,抢占成功则修改锁的状态为1,并设置锁的owner

      若锁状态不为0,则判断是否是重入线程,若是,则再次获取锁,并将锁状态+1

      不公平:


      公平:

      在这里插入图片描述

    • 若tryAcquire没有获取锁:

      addWaiter(Node):新建一个Node节点插入到当前AQS队列的尾部,如没有AQS队列,则新建一个并插入。

      acquireQueued(Node):通过自旋获取锁;或者删除前结点中状态为CANCELLED的节点,接下来获取锁或者通过park阻塞线程。

      注:node是有状态的,分别是CANCELLED,SIGNAL,CONDITION,PROPAGATE,其中ReentrantLock涉及到的状态就是SIGNAL(等待被唤醒),CANCELLED(取消)。

      在这里插入图片描述

      private Node enq(final Node node) {
          //自旋
          for (;;) {
              Node t = tail;
              //尾结点为空 说明AQS链表还没有初始化 那么进行初始化
              if (t == null) { // Must initialize
                  //cas 将AQS的head节点 初始化 成功初始化head之后,将尾结点也初始化
                  //注意 这里我们可以看到head节点是不存储线程信息的 也就是说head节点相当于是一个虚拟节点
                  if (compareAndSetHead(new Node()))
                      tail = head;
              } 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;
              //进入自旋
              for (;;) {
                  //获取当前节点的前一个节点
                  final Node p = node.predecessor();
                  // 如果前一个节点是head 而且再次尝试获取锁成功,将节点从AQS队列中去除 并替换head 同时返回中断标志
                  if (p == head && tryAcquire(arg)) {
                      setHead(node);
                      p.next = null; // help GC
                      failed = false;
                      //注意 只有抢占到锁才会跳出这个for循环
                      return interrupted;
                  }
                  //去除状态为CANCELLED的节点 并且阻塞线程 线程被阻塞在这
                  //注意 线程被唤醒之后继续执行这个for循环 尝试抢占锁 没有抢占到的话又会阻塞
                  if (shouldParkAfterFailedAcquire(p, node) &&
                      parkAndCheckInterrupt())
                      interrupted = true;
              }
          } finally {
              if (failed)
                  //如果失败 将node状态修改为CANCELLED
                  cancelAcquire(node);
          }
      }
      
      
      private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
          int ws = pred.waitStatus;
          if (ws == Node.SIGNAL)
              //如果节点是SIGNAL状态 不需要处理 直接返回
              return true;
          if (ws > 0) {
              //如果节点状态>0 说明节点是取消状态 这种状态的节点需要被清除 用do while循环顺便清除一下前面的连续的、状态为取消的节点
              do {
                  node.prev = pred = pred.prev;
              } while (pred.waitStatus > 0);
              pred.next = node;
          } else {
              //正常的情况下 利用cas将前一个节点的状态替换为 SIGNAL状态 也就是-1
              //注意 这样队列中节点的状态 除了最后一个都是-1 包括head节点
              compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
          }
          return false;
      }
      
      private final boolean parkAndCheckInterrupt() {
          //挂起当前线程 并且返回中断标志  LockSupport.park(thread) 会调用UNSAFE.park()方法将线程阻塞起来(是一个native方法)
          LockSupport.park(this);
          return Thread.interrupted();
      }
      
释放锁
  • 获取锁的状态值,将状态值 -1
  • 判断当前线程是否是锁的owner线程,若不是则抛出异常。
  • 若状态值为0,则锁的owner设为null
  • 通过unpark唤醒头节点下一个状态不为CANCELLED的节点
public final boolean release(int arg) {
    //只有tryRelease返回true 说明已经释放锁 需要将阻塞的线程唤醒 否则不需要唤醒别的线程
    if (tryRelease(arg)) {
        Node h = head;
        //如果头结点不为空 而且状态不为0 代表同步队列已经初始化 且存在需要唤醒的node
        //注意 同步队列的头结点相当于是一个虚拟节点  这一点我们可以在构建节点的代码中很清楚的知道
        //并且在shouldParkAfterFailedAcquire方法中 会把head节点的状态修改为-1
        //如果head的状态为0 那么代表队列中没有需要被唤醒的元素 直接返回true
        if (h != null && h.waitStatus != 0)
            //唤醒头结点的下一个节点
            unparkSuccessor(h);
        return true;
    }
    return false;
}
protected final boolean tryRelease(int releases) {
    //减少重入次数
    int c = getState() - releases;
    //如果获取锁的线程不是当前线程 抛出异常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    //如果state为0 说明已经彻底释放了锁 返回true
    if (c == 0) {
        free = true;
        //将获取锁的线程设置为null
        setExclusiveOwnerThread(null);
    }
    //重置state的值
    setState(c);
    //如果释放了锁 就返回true 否则返回false
    return free;
}
private void unparkSuccessor(Node node) {
    //获取头结点的状态 将头结点状态设置为0 代表现在正在有线程被唤醒 如果head状态为0 就不会进入这个方法了
    int ws = node.waitStatus;
    if (ws < 0)
        //将头结点状态设置为0
        compareAndSetWaitStatus(node, ws, 0);
    //唤醒头结点的下一个状态不是cancelled的节点 (因为头结点是不存储阻塞线程的)
    Node s = node.next;
    //当前节点是null 或者是cancelled状态
    if (s == null || s.waitStatus > 0) {
        s = null;
        //从aqs链表的尾部开始遍历 找到离头结点最近的 不为空的 状态不是cancelled的节点 赋值给s 这里为什么从尾结点开始遍历而是头结点 应该是因为添加结点的时候是先初始化结点的prev的, 从尾结点开始遍历 不会出现prve没有赋值的情况 
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        //调用LockSupport.unpark()唤醒指定的线程
        LockSupport.unpark(s.thread);
}	
可重入
static final class NonfairSync extends Sync {
    // ...

    // Sync 继承过来的方法
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        // 如果已经获得了锁, 线程还是当前线程, 表示发生了锁重入
        else if (current == getExclusiveOwnerThread()) {
            // state++
            int nextc = c + acquires;
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }

    // Sync 继承过来的方法
    protected final boolean tryRelease(int releases) {
        // state--
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        // 支持锁重入, 只有 state 减为 0, 才释放成功
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
}

可打断

不可打断模式:在此模式下,即使它被打断,仍会驻留在 AQS 队列中,一直要等到获得锁后方能得知自己被打断了

// Sync 继承自 AQS
static final class NonfairSync extends Sync {
    // ...

    private final boolean parkAndCheckInterrupt() {
        // 如果打断标记已经是 true, 则 park 会失效
        LockSupport.park(this);
        // interrupted 会清除打断标记
        return Thread.interrupted();
    }

    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;
                    failed = false;
                    // 还是需要获得锁后, 才能返回打断状态
                    return interrupted;
                }
                if (
                        shouldParkAfterFailedAcquire(p, node) &&
                                parkAndCheckInterrupt()
                ) {
                    // 如果是因为 interrupt 被唤醒, 返回打断状态为 true
                    interrupted = true;
                }
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

    public final void acquire(int arg) {
        if (
                !tryAcquire(arg) &&
                        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
        ) {
            // 如果打断状态为 true
            selfInterrupt();
        }
    }

    static void selfInterrupt() {
        // 重新产生一次中断,这时候线程是如果正常运行的状态,那么不是出于sleep等状态,interrupt方法就不会报错
        Thread.currentThread().interrupt();
    }
}
}

可打断模式

static final class NonfairSync extends Sync {
    public final void acquireInterruptibly(int arg) throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        // 如果没有获得到锁, 进入 ㈠
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }

    // ㈠ 可打断的获取锁流程
    private void doAcquireInterruptibly(int arg) throws InterruptedException {
        final Node node = addWaiter(Node.EXCLUSIVE);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt()) {
                    // 在 park 过程中如果被 interrupt 会进入此
                    // 这时候抛出异常, 而不会再次进入 for (;;)
                    throw new InterruptedException();
                }
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
}

公平锁

其实这两种实现的差别只有两点(前面的原理的获取锁中我们已经讲到了):

  1. 在lock()方法开始的时候非公平锁会尝试cas抢占锁插一次队;
  2. 在tryAcquire()方法发现state为0的时候,非公平锁会抢占一次锁,而公平锁会判断AQS链表中是否存在等待的线程,没有等待的线程才会去抢占锁。
条件变量实现原理
图解流程

每个条件变量其实就对应着一个等待队列,其实现类是 ConditionObject

await 流程
开始 Thread-0 持有锁,调用 await,进入 ConditionObject 的 addConditionWaiter 流程
创建新的 Node 状态为 -2(Node.CONDITION),关联 Thread-0,加入等待队列尾部

1595079373121

接下来进入 AQS 的 fullyRelease 流程,释放同步器上的锁

1595079397323

unpark AQS 队列中的下一个节点,竞争锁,假设没有其他竞争线程,那么 Thread-1 竞争成功

1595079457815

park 阻塞 Thread-0

1595079481112

signal 流程

假设 Thread-1 要来唤醒 Thread-0

1595079499939

进入 ConditionObject 的 doSignal 流程,取得等待队列中第一个 Node,即 Thread-0 所在 Node

1595079518690

执行 transferForSignal 流程,将该 Node 加入 AQS 队列尾部,将 Thread-0 的 waitStatus 改为 0,Thread-3 的waitStatus 改为 -1

1595079772187

Thread-1 释放锁,进入 unlock 流程,略

源码分析
public class ConditionObject implements Condition, java.io.Serializable {
    private static final long serialVersionUID = 1173984872572414699L;

    // 第一个等待节点
    private transient Node firstWaiter;

    // 最后一个等待节点
    private transient Node lastWaiter;
    public ConditionObject() { }
    // ㈠ 添加一个 Node 至等待队列
    private Node addConditionWaiter() {
        Node t = lastWaiter;
        // 所有已取消的 Node 从队列链表删除, 见 ㈡
        if (t != null && t.waitStatus != Node.CONDITION) {
            unlinkCancelledWaiters();
            t = lastWaiter;
        }
        // 创建一个关联当前线程的新 Node, 添加至队列尾部
        Node node = new Node(Thread.currentThread(), Node.CONDITION);
        if (t == null)
            firstWaiter = node;
        else
            t.nextWaiter = node;
        lastWaiter = node;
        return node;
    }
    // 唤醒 - 将没取消的第一个节点转移至 AQS 队列
    private void doSignal(Node first) {
        do {
            // 已经是尾节点了
            if ( (firstWaiter = first.nextWaiter) == null) {
                lastWaiter = null;
            }
            first.nextWaiter = null;
        } while (
            // 将等待队列中的 Node 转移至 AQS 队列, 不成功且还有节点则继续循环 ㈢
                !transferForSignal(first) &&
                        // 队列还有节点
                        (first = firstWaiter) != null
        );
    }

    // 外部类方法
    // ㈢ 如果节点状态是取消, 返回 false 表示转移失败, 否则转移成功
    final boolean transferForSignal(Node node) {
        // 设置当前node状态为0(因为处在队列末尾),如果状态已经不是 Node.CONDITION, 说明被取消了
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;
        // 加入 AQS 队列尾部
        Node p = enq(node);
        int ws = p.waitStatus;
        if (
            // 插入节点的上一个节点被取消
                ws > 0 ||
                        // 插入节点的上一个节点不能设置状态为 Node.SIGNAL
                        !compareAndSetWaitStatus(p, ws, Node.SIGNAL)
        ) {
            // unpark 取消阻塞, 让线程重新同步状态
            LockSupport.unpark(node.thread);
        }
        return true;
    }
// 全部唤醒 - 等待队列的所有节点转移至 AQS 队列
private void doSignalAll(Node first) {
    lastWaiter = firstWaiter = null;
    do {
        Node next = first.nextWaiter;
        first.nextWaiter = null;
        transferForSignal(first);
        first = next;
    } while (first != null);
}

    // ㈡
    private void unlinkCancelledWaiters() {
        // ...
    }
    // 唤醒 - 必须持有锁才能唤醒, 因此 doSignal 内无需考虑加锁
    public final void signal() {
        // 如果没有持有锁,会抛出异常
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        Node first = firstWaiter;
        if (first != null)
            doSignal(first);
    }
    // 全部唤醒 - 必须持有锁才能唤醒, 因此 doSignalAll 内无需考虑加锁
    public final void signalAll() {
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        Node first = firstWaiter;
        if (first != null)
            doSignalAll(first);
    }
    // 不可打断等待 - 直到被唤醒
    public final void awaitUninterruptibly() {
        // 添加一个 Node 至等待队列, 见 ㈠
        Node node = addConditionWaiter();
        // 释放节点持有的锁, 见 ㈣
        int savedState = fullyRelease(node);
        boolean interrupted = false;
        // 如果该节点还没有转移至 AQS 队列, 阻塞
        while (!isOnSyncQueue(node)) {
            // park 阻塞
            LockSupport.park(this);
            // 如果被打断, 仅设置打断状态
            if (Thread.interrupted())
                interrupted = true;
        }
        // 唤醒后, 尝试竞争锁, 如果失败进入 AQS 队列
        if (acquireQueued(node, savedState) || interrupted)
            selfInterrupt();
    }
    // 外部类方法
    // ㈣ 因为某线程可能重入,需要将 state 全部释放,获取state,然后把它全部减掉,以全部释放
    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;
        }
    }
    // 打断模式 - 在退出等待时重新设置打断状态
    private static final int REINTERRUPT = 1;
    // 打断模式 - 在退出等待时抛出异常
    private static final int THROW_IE = -1;
    // 判断打断模式
    private int checkInterruptWhileWaiting(Node node) {
        return Thread.interrupted() ?
                (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
                0;
    }
    // ㈤ 应用打断模式
    private void reportInterruptAfterWait(int interruptMode)
            throws InterruptedException {
        if (interruptMode == THROW_IE)
            throw new InterruptedException();
        else if (interruptMode == REINTERRUPT)
            selfInterrupt();
    }
    // 等待 - 直到被唤醒或打断
    public final void await() throws InterruptedException {
        if (Thread.interrupted()) {
            throw new InterruptedException();
        }
        // 添加一个 Node 至等待队列, 见 ㈠
        Node node = addConditionWaiter();
        // 释放节点持有的锁
        int savedState = fullyRelease(node);
        int interruptMode = 0;
        // 如果该节点还没有转移至 AQS 队列, 阻塞
        while (!isOnSyncQueue(node)) {
            // park 阻塞              
            LockSupport.park(this);
            // 如果被打断, 退出等待队列
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
        }
        // 退出等待队列后, 还需要获得 AQS 队列的锁
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        // 所有已取消的 Node 从队列链表删除, 见 ㈡
        if (node.nextWaiter != null)
            unlinkCancelledWaiters();
        // 应用打断模式, 见 ㈤
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
    }
    // 等待 - 直到被唤醒或打断或超时
    public final long awaitNanos(long nanosTimeout) throws InterruptedException {
        if (Thread.interrupted()) {
            throw new InterruptedException();
        }
        // 添加一个 Node 至等待队列, 见 ㈠
        Node node = addConditionWaiter();
        // 释放节点持有的锁
        int savedState = fullyRelease(node);
        // 获得最后期限
        final long deadline = System.nanoTime() + nanosTimeout;
        int interruptMode = 0;
        // 如果该节点还没有转移至 AQS 队列, 阻塞
        while (!isOnSyncQueue(node)) {
            // 已超时, 退出等待队列
            if (nanosTimeout <= 0L) {
                transferAfterCancelledWait(node);
                break;
            }
            // park 阻塞一定时间, spinForTimeoutThreshold 为 1000 ns
            if (nanosTimeout >= spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);
            // 如果被打断, 退出等待队列
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
            nanosTimeout = deadline - System.nanoTime();
        }
        // 退出等待队列后, 还需要获得 AQS 队列的锁
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        // 所有已取消的 Node 从队列链表删除, 见 ㈡
        if (node.nextWaiter != null)
            unlinkCancelledWaiters();
        // 应用打断模式, 见 ㈤
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
        return deadline - System.nanoTime();
    }
    // 等待 - 直到被唤醒或打断或超时, 逻辑类似于 awaitNanos
    public final boolean awaitUntil(Date deadline) throws InterruptedException {
        // ...
    }
    // 等待 - 直到被唤醒或打断或超时, 逻辑类似于 awaitNanos
    public final boolean await(long time, TimeUnit unit) throws InterruptedException {
        // ...
    }
    // 工具方法 省略 ...
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值