【并发编程】回环栅栏CyclicBarrier从入门到源码精通

CyclicBarrier是什么

  • 字面意思回环栅栏。直接翻译是循环屏障。
  • 通过它可以实现让一组线程等待至某个状态(屏障点)之后再全部同时执行。
  • 叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。
  • 通过ReentrantLock的"独占锁"和Conditon来实现一组线程的阻塞唤醒。

栅栏.png

CyclicBarrier的使用场景

  • 用于多线程计算数据,最后合并计算结果的场景。
  • CyclicBarrier的计数器能够重置,屏障可以重复使用的特性,可以支持类似“人满发车”的场景。

CountDownLatch与CyclicBarrier的区别

  • CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次
  • CyclicBarrier还提供getNumberWaiting(可以获得CyclicBarrier阻塞的线程数量)、isBroken(用来知道阻塞的线程是否被中断)等方法。
  • CountDownLatch会阻塞主线程,CyclicBarrier不会阻塞主线程,只会阻塞子线程。
  • CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同。
  • CountDownLatch一般用于一个或多个线程,等待其他线程执行完任务后,再执行。CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行。
  • CyclicBarrier 还可以提供一个 barrierAction,合并多线程计算结果。
  • CyclicBarrier是通过ReentrantLock的"独占锁"和Conditon来实现一组线程的阻塞唤醒的,而CountDownLatch则是通过AQS的“共享锁”实现

CyclicBarrier的常用方法

// 构造方法参数:parties表示屏障拦截的线程数量,每个线程调用 await 方法告诉 CyclicBarrier 我已经到达了屏障,然后当前线程被阻塞。
// 构造方法参数:barrierAction表示在线程到达屏障时,优先执行 barrierAction,方便处理更复杂的业务场景(该线程的执行时机是在到达屏障之后再执行)
public CyclicBarrier(int parties)
public CyclicBarrier(int parties, Runnable barrierAction)

// 屏障:指定数量的线程全部调用await()方法时,这些线程不再阻塞
// BrokenBarrierException 表示栅栏已经被破坏,破坏的原因可能是其中一个线程 await() 时被中断或者超时
public int await() throws InterruptedException, BrokenBarrierException
public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException

// 循环:通过reset()方法可以进行重置到原始状态
public void reset()

CyclicBarrier的合并计算场景

import java.util.Set;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CyclicBarrierTest1 {
	// 保存每个学生的平均成绩
	private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();

	// 定义线程池
	private ExecutorService threadPool = Executors.newFixedThreadPool(5);

	// 定义CyclicBarrier(回环栅栏)
	private CyclicBarrier cb = new CyclicBarrier(5, () -> {
		int result = 0;
		Set<String> set = map.keySet();
		for (String s : set) {
			result += map.get(s);
		}
		System.out.println("这五人的平均成绩为:" + (result / 5) + "分");
	});

	public void count() {
		for (int i = 0; i < 5; i++) {
			threadPool.execute(new Runnable() {

				@Override
				public void run() {
					// 获取学生平均成绩
					int score = (int) (Math.random() * 40 + 60);
					// 放到共享变量中
					map.put(Thread.currentThread().getName(), score);
					// 打印当前层级
					System.out.println(Thread.currentThread().getName() + "同学的平均成绩为:" + score);
					try {
						// 执行完运行await(),等待所有学生平均成绩都计算完毕
						cb.await();
					} catch (InterruptedException | BrokenBarrierException e) {
						e.printStackTrace();
					}
				}

			});
		}
	}

	public static void main(String[] args) {
		CyclicBarrierTest1 cb = new CyclicBarrierTest1();
		cb.count();
	}
}
  • 运行结果:在所有人分数得到后,才会汇总结果
    合并计算结果.png

CyclicBarrier的“人满发车”场景

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class CyclicBarrierTest1 {
	public static void main(String[] args) {

		// 定义一个原子计数器
		AtomicInteger counter = new AtomicInteger();
		// 定义线程池
		ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 1000, TimeUnit.SECONDS,
				new ArrayBlockingQueue<>(100), (r) -> new Thread(r, counter.addAndGet(1) + " 号 "),
				new ThreadPoolExecutor.AbortPolicy());

		// 定义CyclicBarrier(回环栅栏)
		CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> System.out.println("裁判:比赛开始~~"));

		// 提交14个线程
		for (int i = 0; i < 14; i++) {
			threadPoolExecutor.submit(new Runner(cyclicBarrier));
		}

	}

	/**
	 * 具体的任务
	 */
	static class Runner extends Thread {

		private CyclicBarrier cyclicBarrier;

		public Runner(CyclicBarrier cyclicBarrier) {
			this.cyclicBarrier = cyclicBarrier;
		}

		@Override
		public void run() {
			try {
				// 得到一个随机的睡眠时间进行睡眠
				int sleepMills = ThreadLocalRandom.current().nextInt(1000);
				Thread.sleep(sleepMills);
				// 睡眠结束后,准备就绪
				System.out.println(Thread.currentThread().getName() + " 选手已就位, 准备共用时: " + sleepMills + "ms"
						+ cyclicBarrier.getNumberWaiting());
				// 等待发令枪
				cyclicBarrier.await();

			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				e.printStackTrace();
			}
		}
	}
}
  • 运行结果:每满5人执行一次发车操作!最后一次不足5人,不发车!

“人满发车”运行结果.png

CyclicBarrier源码分析的关注点

  • 一组线程在触发屏障之前互相等待,最后一个线程到达屏障后唤醒逻辑是如何实现的。
  • 删栏循环使用是如何实现的。
  • 条件队列到同步队列的转换实现逻辑。

CyclicBarrier源码整体流程

  • 第一阶段(await):获取锁(刚进入方法),释放锁,进入条件队列,阻塞线程
  • 第二阶段(singal/singalAll):被其他调用singal/singalAll的线程唤醒,条件队列转同步队列,在释放锁的时候唤醒头结点的后续节点所在线程
  • 第三阶段:获取锁(同步队列,有竞争还会阻塞),释放锁(唤醒头结点的后续节点所在线程)

CyclicBarrier的构造方法源码分析

/**
 * 参数只有parties,他直接调用另一个构造方法
 */
public CyclicBarrier(int parties) {
    this(parties, null);
}

/**
 * 俩个参数的构造方法
 * parties:表示屏障拦截的线程数量,每个线程调用 await 方法告诉 CyclicBarrier 我已经到达了屏障,然后当前线程被阻塞。
 * barrierAction:表示在线程到达屏障时,优先执行 barrierAction,方便处理更复杂的业务场景(该线程的执行时机是在到达屏障之后再执行)
 */
public CyclicBarrier(int parties, Runnable barrierAction) {
    // 传入的需要拦截的线程数为0,直接抛出异常
    if (parties <= 0) throw new IllegalArgumentException();
    // 指定线程数副本,用于重置
    this.parties = parties;
    // 指定计数器,真正的计数器
    this.count = parties;
    // 定义到达屏障后的触发机制
    this.barrierCommand = barrierAction;
}

CyclicBarrier的等待方法await()源码分析

/**
 * 无参数的等待方法:主要调用dowait方法
 */
public int await() throws InterruptedException, BrokenBarrierException {
    try {
        return dowait(false, 0L);
    } catch (TimeoutException toe) {
        throw new Error(toe); // cannot happen
    }
}

/**
 * 有参数的等待方法:主要调用dowait方法
 */
public int await(long timeout, TimeUnit unit)
    throws InterruptedException,
           BrokenBarrierException,
           TimeoutException {
    return dowait(true, unit.toNanos(timeout));
}

/**
 * 等待的实际执行逻辑
 */
private int dowait(boolean timed, long nanos)
    throws InterruptedException, BrokenBarrierException,
           TimeoutException {
    // 得到他本身的变量:ReentrantLock锁
    final ReentrantLock lock = this.lock;
    // 加锁:调用await,外层一定是ReentrantLock。调用wait,外层一定是synchronized。
    lock.lock();
    try {
        // 定义一个代数的对象。经过一次栅栏,便是一代。
        final Generation g = generation;
        // 判断栅栏是否已经被破坏
        if (g.broken)
            throw new BrokenBarrierException();

        // 判断当前线程是否被中断
        if (Thread.interrupted()) {
            // 冲破屏障的逻辑:设置当前代的broken状态为true,重置栅栏需要的线程数量,唤醒所有线程。
            breakBarrier();
            // 抛出中断异常
            throw new InterruptedException();
        }

        // 将计数器减一,并且赋值给index变量
        int index = --count;
        // 计数器较少到0(最后一个到达屏障的线程),准备打开栅栏的逻辑。唤醒所有线程,并准备下一代。
        if (index == 0) {  // tripped
            // 定义任务被执行的标记为false
            boolean ranAction = false;
            try {
                // 拿到要在跳过栅栏之后的逻辑。(构造方法的第二个参数)
                final Runnable command = barrierCommand;
                // 方法存在,执行方法!
                if (command != null)
                    command.run();
                // 变更任务被执行的标记为true
                ranAction = true;
                // 更新栅栏的状态并唤醒所有线程
                nextGeneration();
                // 返回结果:0
                return 0;
            } finally {
                // 如果栅栏的任务失败了
                if (!ranAction)
                    // 冲破屏障的逻辑:设置当前代的broken状态为true,重置栅栏需要的线程数量,唤醒所有线程。
                    breakBarrier();
            }
        }

        // loop until tripped, broken, interrupted, or timed out
        // 循环直到跳闸、中断、中断或超时
        for (;;) {
            try {
                // 超时等待的逻辑判断:await无参的时候为false,有参数的时候为true
                if (!timed)
                    // 调用条件队列的入队阻塞,直到被中断或者被唤醒。
                    // private final Condition trip = lock.newCondition(); // 从ReentrantLock获取的条件队列
                    trip.await();
                else if (nanos > 0L)
                    // 带有超时条件的等待操作
                    nanos = trip.awaitNanos(nanos);
            } catch (InterruptedException ie) {
                // 如果还是当前代,并且没有被冲破屏障
                if (g == generation && ! g.broken) {
                    // 冲破屏障的逻辑:设置当前代的broken状态为true,重置栅栏需要的线程数量,唤醒所有线程。
                    breakBarrier();
                    // 抛出当前的异常
                    throw ie;
                } else {
                    // We're about to finish waiting even if we had not
                    // been interrupted, so this interrupt is deemed to
                    // "belong" to subsequent execution.
                    // 中断当前线程
                    Thread.currentThread().interrupt();
                }
            }

            // 判断栅栏是否已经被破坏
            if (g.broken)
                throw new BrokenBarrierException();

            // 判断当前是否已经进入下一代
            if (g != generation)
                return index;

            // 超时等待的逻辑判断:await无参的时候为false,有参数的时候为true,并且需要等待的超时时间小于0
            if (timed && nanos <= 0L) {
                // 冲破屏障的逻辑:设置当前代的broken状态为true,重置栅栏需要的线程数量,唤醒所有线程。
                breakBarrier();
                // 抛出超时异常
                throw new TimeoutException();
            }
        }
    } finally {
        // 解锁操作
        lock.unlock();
    }
}

/**
 * 冲破屏障的逻辑
 */
private void breakBarrier() {
    // 将破坏标志变为true
    generation.broken = true;
    // 将屏障的线程数量还原
    count = parties;
    // 去唤醒其他呗拦截的线程
    trip.signalAll();
}

/**
 * 更新栅栏的状态并唤醒所有线程
 */
private void nextGeneration() {
    // signal completion of last generation
    // 不知道要唤醒哪一个,所以唤醒全部。上一代信号完成,唤醒其他的线程
    trip.signalAll();
    // set up next generation
    // 还原下一代的数量。循环利用的关键点!
    count = parties;
    // 重新定义代数(下一代)
    generation = new Generation();
}

使用到的条件队列的入队阻塞方法:await()源码分析

/**
 * 条件队列的入队阻塞操作
 */
public final void await() throws InterruptedException {
    // 线程被中断,抛出中断异常
    if (Thread.interrupted())
        throw new InterruptedException();
    // 添加节点在条件等待队列中
    Node node = addConditionWaiter();
    // 释放锁逻辑
    int savedState = fullyRelease(node);
    // 定义中断模式状态为0
    int interruptMode = 0;
    // 判断是否在同步队列中
    while (!isOnSyncQueue(node)) {
        // 不在同步队列中,阻塞当前线程
        LockSupport.park(this);
        // 在等待的时候中断,返回不为0,说明被中断过,直接跳出循环
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    // acquireQueued获取执行权的逻辑:获取到资源(false),被中断(true)
    // 并且interruptMode在上方处理中是条件队列转化为同步队列
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        // 将值变为重新中断
        interruptMode = REINTERRUPT;
    // 下一个节点不为空的时候,清理掉链表中的状态为cancelled(1:已取消)状态的节点
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    // 状态被中断过,处理重新中断或者抛异常
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}

/**
 * 条件队列的入队阻塞操作,带有超时校验
 */
public final long awaitNanos(long nanosTimeout)
        throws InterruptedException {
    // 线程被中断,抛出中断异常
    if (Thread.interrupted())
        throw new InterruptedException();
    // 添加节点在条件等待队列中
    Node node = addConditionWaiter();
    // 释放锁逻辑
    int savedState = fullyRelease(node);
    // 计算最后期限
    final long deadline = System.nanoTime() + nanosTimeout;
    // 定义中断模式状态为0
    int interruptMode = 0;
    // 判断是否在同步队列中
    while (!isOnSyncQueue(node)) {
        // 超时时间小于0
        if (nanosTimeout <= 0L) {
            // 转换(条件转同步)之后的取消等待逻辑
            transferAfterCancelledWait(node);
            // 跳出循环
            break;
        }
        // 超时时间大于1000纳秒
        if (nanosTimeout >= spinForTimeoutThreshold)
            // 直接进行阻塞操作
            LockSupport.parkNanos(this, nanosTimeout);
        // 在等待的时候中断,返回不为0,说明被中断过,直接跳出循环
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
        // 重新计算剩余的超时时间
        nanosTimeout = deadline - System.nanoTime();
    }
    // acquireQueued获取执行权的逻辑:获取到资源(false),被中断(true)
    // 并且interruptMode在上方处理中是条件队列转化为同步队列
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    // 下一个节点不为空的时候,清理掉链表中的状态为cancelled(1:已取消)状态的节点
    if (node.nextWaiter != null)
        unlinkCancelledWaiters();
    // 状态被中断过,处理重新中断或者抛异常
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
    // 返回相对于超时时间的剩余时间
    return deadline - System.nanoTime();
}

/**
 * 添加节点在条件等待队列(单向列表)中
 */
private Node addConditionWaiter() {
    // 得到最后一个节点
    Node t = lastWaiter;
    // If lastWaiter is cancelled, clean out.
    // 去后一个节点不是空,并且状态不是-2(当前节点在等待condition,也就是在condition队列中)
    if (t != null && t.waitStatus != Node.CONDITION) {
        // 清理掉链表中的状态为cancelled(1:已取消)状态的节点
        unlinkCancelledWaiters();
        t = lastWaiter;
    }
    // 创建一个新的当前节点
    Node node = new Node(Thread.currentThread(), Node.CONDITION);
    // 单向列表的头结点为空,设置当前节点为头结点
    if (t == null)
        firstWaiter = node;
    else
        // 单向列表的头结点不为空,设置头结点的下一个节点是当前节点
        t.nextWaiter = node;
    // 设置单向列表的最后一个节点为当前节点
    lastWaiter = node;
    // 返回当前的节点
    return node;
}

/**
 * 清理掉链表中的状态为cancelled(1:已取消)状态的节点
 */
private void unlinkCancelledWaiters() {
    // 得到第一个节点
    Node t = firstWaiter;
    // 定义标量trail
    Node trail = null;
    // 第一个节点不是空的
    while (t != null) {
        // 得到第二个节点
        Node next = t.nextWaiter;
        // 第二个节点不是-2(当前节点在等待condition,也就是在condition队列中)
        if (t.waitStatus != Node.CONDITION) {
            // 头结点的下一个节点变为null
            t.nextWaiter = null;
            // 如果trail为空,将断掉的节点复制到trail上。
            if (trail == null)
                firstWaiter = next;
            else
                // 如果trail不为空,将trail的下一个节点设置为当前节点
                trail.nextWaiter = next;
            // 第二个节点是空的,设置条件队列的尾结点为trail
            if (next == null)
                lastWaiter = trail;
        }
        else
            // 节点是-2,标识节点在条件队列中,直接赋值
            trail = t;
        // 将下一个节点设置到t上,为了继续循环
        t = next;
    }
}

/**
 * 释放锁逻辑
 */
final int fullyRelease(Node node) {
    // 定时失败标志位true
    boolean failed = true;
    try {
        // 获取state:在CyclicBarrier中表示的是重入次数
        int savedState = getState();
        // 释放重入锁
        if (release(savedState)) {
            // 释放锁成功
            // 失败标记为false
            failed = false;
            // 返回重入次数
            return savedState;
        } else {
            // 释放锁失败,抛异常
            throw new IllegalMonitorStateException();
        }
    } finally {
        // 当前线程失败了
        if (failed)
            // 设置这个节点的状态为取消状态
            node.waitStatus = Node.CANCELLED;
    }
}

/**
 * 释放重入锁
 */
public final boolean release(int arg) {
    // 尝试去释放:true为全部释放完成,false为没有全部释放完成
    if (tryRelease(arg)) {
        // 得到同步等待队列的头结点
        Node h = head;
        // 同步等待队列的头结点不是空,并且不在运行中,进行unpark操作。
        if (h != null && h.waitStatus != 0)
            // unpark去唤醒同步队列中的下一个线程
            unparkSuccessor(h);
        // 返回释放锁成功
        return true;
    }
    // 返回释放锁失败
    return false;
}

/**
 * 尝试去释放锁:true为全部释放完成,false为没有全部释放完成
 */
protected final boolean tryRelease(int releases) {
    // 当前的重入次数减去需要释放的重入次数
    int c = getState() - releases;
    // 判断当前线程不是持有锁的线程,抛出异常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    // 定义释放成功标记为false
    boolean free = false;
    // 如果锁都释放完了
    if (c == 0) {
        // 设置释放成功标记为true
        free = true;
        // 清空当前正在执行的线程
        setExclusiveOwnerThread(null);
    }
    // 设置重入次数为最终释放的状态
    setState(c);
    // 返回是否全部释放锁的标记
    return free;
}

/**
 * unpark去唤醒下一个队列的线程
 */
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;
    // SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
    // CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;
    // PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
    // 对于ReentrantLock,只能出现-1的状态
    if (ws < 0)
        // 通过CAS的方式,设置线程状态为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;
    // 下一个节点为空或者下一个节点的状态大于0(CANCELLED,值为1,表示当前的线程被取消;)
    if (s == null || s.waitStatus > 0) {
        // 下一个节点直接置为null
        s = null;
        // 从尾部开始向前遍历,找到最前的一个处于正常阻塞状态的结点,直到节点重合
        // 从尾部遍历的原因是为了防止在高并发场景下漏掉线程
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    // 过滤后的下一个节点不为null,唤醒他
    if (s != null)
        // 唤醒下一个节点
        LockSupport.unpark(s.thread);
}

/**
 * 判断是否在同步队列中
 */
final boolean isOnSyncQueue(Node node) {
    // 当前节点的状态为-2(当前节点在等待condition,也就是在condition队列中),肯定不在同步队列中,返回false
    // 当前节点的前驱节点是null:同步队列是双向链表,所以肯定不是同步队列
    if (node.waitStatus == Node.CONDITION || node.prev == null)
        return false;
    // 当前节点的next有值,说明是同步队列。
    // 同步队列特有的下一个节点为next,上一个节点为prev。条件队列(单向列表)只有下一个节点nextWaiter。条件队列(单向列表)的next和prev没有值!。
    if (node.next != null) // If has successor, it must be on queue
        return true;
    /*
     * node.prev can be non-null, but not yet on queue because
     * the CAS to place it on queue can fail. So we have to
     * traverse from tail to make sure it actually made it.  It
     * will always be near the tail in calls to this method, and
     * unless the CAS failed (which is unlikely), it will be
     * there, so we hardly ever traverse much.
     */
    // 从同步队列的队尾元素一直往前找节点
    return findNodeFromTail(node);
}

/**
 * 从同步队列的队尾元素一直往前找节点
 */
private boolean findNodeFromTail(Node node) {
    // 得到同步队列的尾结点
    Node t = tail;
    // 从队尾元素一直往前找,找到一个相同的节点就表名他在同步队列中。t == null说明没有节点可以继续找,他不在同步队列中
    for (;;) {
        if (t == node)
            return true;
        if (t == null)
            return false;
        t = t.prev;
    }
}

/**
 * 检测在等待的时候中断
 */
private int checkInterruptWhileWaiting(Node node) {
    // 没有被中断:直接返回0
    // 被中断:判断后续的处理应该是THROW_IE(抛出InterruptedException)还是REINTERRUPT(重新中断)。
    return Thread.interrupted() ?
        (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
        0;
}

/**
 * 转换(条件转同步)之后的取消等待逻辑:条件队列转同步队列成功,返回true。
 */
final boolean transferAfterCancelledWait(Node node) {
    // CAS尝试将条件队列中的值变为正在执行状态
    if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
        // 入同步队列
        enq(node);
        // 返回true
        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 被触发了 signal 方法时,node 就会被加到同步队列上
    // 一直判断节点在不在同步队列上,直到调用出现在同步队列中,跳出循环
    while (!isOnSyncQueue(node))
        Thread.yield();
    // 返回false
    return false;
}

/**
 * 设计精髓:100%创建队列或者100%入队
 */
private Node enq(final Node node) {
    for (;;) {
        // 得到之前的尾节点
        Node t = tail;
        // 之前的尾节点为空,需要进行初始化队列
        if (t == null) { // Must initialize
            // 通过CAS的方式将头节点设置为当前节点
            if (compareAndSetHead(new Node()))
                // 头结点设置成功后,复制给尾节点。只有一个节点的状态
                tail = head;
        } else {
            // 设置当前线程节点的上一个节点是之前的尾节点
            node.prev = t;
            // cas尝试将尾节点设置为当前线程的节点
            if (compareAndSetTail(t, node)) {
                // 设置之前尾节点,现在的倒数第二个节点的下一个节点是当前线程节点
                t.next = node;
                // 返回当前节点!注意:这里是这个方法唯一返回的地方!也就是说初始化后还会继续循环一次来设置上一个下一个节点,然后进行返回。
                return t;
            }
        }
    }
}

/**
 * 根据中断状态标志去处理后续逻辑
 */
private void reportInterruptAfterWait(int interruptMode)
    throws InterruptedException {
    // THROW_IE抛出中断异常
    if (interruptMode == THROW_IE)
        throw new InterruptedException();
    // REINTERRUPT重新中断
    else if (interruptMode == REINTERRUPT)
        selfInterrupt();
}

/**
 * 线程自我中断
 */
static void selfInterrupt() {
    Thread.currentThread().interrupt();
}

/**
 * 阻塞逻辑,多了时间的校验
 */
public static void parkNanos(Object blocker, long nanos) {
    if (nanos > 0) {
        // 得到当前线程
        Thread t = Thread.currentThread();
        // 设置被谁阻塞
        setBlocker(t, blocker);
        // 阻塞当前线程
        UNSAFE.park(false, nanos);
        // 唤醒后,清空blocker信息
        setBlocker(t, null);
    }
}

/**
 * 最终的解锁操作
 */
public void unlock() {
    sync.release(1);
}

/**
 * 最终的解锁操作
 */
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

同步队列获取执行权:acquireQueued(final Node node, int arg) 源码分析

/**
 * 获取执行权的逻辑:这里的精髓点是一定能保证线程要么获取到资源(false),要么被中断(true)
 */
final boolean acquireQueued(final Node node, int arg) {
    // 设置失败的标志位为true
    boolean failed = true;
    try {
        // 设置中断的标记位为false
        boolean interrupted = false;
        for (;;) {
            // 得到当前节点的上一个(前置)节点,前置节点为null,会抛出空指针异常
            final Node p = node.predecessor();
            // 如果前置节点是头结点,并且尝试执行当前线程成功
            if (p == head && tryAcquire(arg)) {
                // 可以执行了,就把当前节点设置为头结点
                setHead(node);
                // 前置节点去掉引用,方便GC去回收
                p.next = null; // help GC
                // 设置失败的标志位为false
                failed = false;
                // 返回中断的标记位:false
                return interrupted;
            }
            // 代码执行到这里,说明尝试获取锁,但是获取锁失败了。
            // 阻塞前的准备工作操作成功(状态是-1的时候成功)
            // 将线程阻塞,等待他去唤醒。唤醒后返回线程的中断状态!
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                // 设置中断的标记位:false
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

/**
 * 获取锁失败后的准备逻辑,阻塞前的准备逻辑
 */
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    // 获取前驱当前节点的等待状态
    int ws = pred.waitStatus;
    // 状态为-1的时候:当前节点的后继节点包含的线程需要运行,也就是unpark;
    if (ws == Node.SIGNAL)
        /*
        * This node has already set status asking a release
        * to signal it, so it can safely park.
        */
        // 前驱当前节点状态已经设置为SIGNAL,可以进行安全的阻塞
        return true;
    // 大于0(CANCELLED状态):表示当前的线程被取消;
    if (ws > 0) {
        /*
        * Predecessor was cancelled. Skip over predecessors and
        * indicate retry.
        */
        // 前驱节点已经因为超时或响应了中断,需要跳过这些状态大于0的节点,直到找到一个状态不是大于0的。
        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.
        */
        // 针对于ReentrantLock,到这里的状态只能为0或者PROPAGATE(-3)
        // 通过CAS将前置节点的状态设置为SIGNAL(-1)
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

/**
 * 状态为SIGNAL(-1)成功。他需要排队,所以直接调用park方法进行阻塞
 */
private final boolean parkAndCheckInterrupt() {
    // 阻塞
    LockSupport.park(this);
    // unpark之后,返回当前的中断状态,并清除中断标志位
    return Thread.interrupted();
}

/**
 * 取消获取锁的逻辑
 */
private void cancelAcquire(Node node) {
    // Ignore if node doesn't exist
    // 忽略节点不存在的时候
    if (node == null)
        return;

    // 设置当前节点的线程为null
    node.thread = null;

    // Skip cancelled predecessors:有前驱节点被取消,跳过所有被取消的
    // 得到前驱节点
    Node pred = node.prev;
    // 前驱节点的状态大于0,被取消
    while (pred.waitStatus > 0)
        // 将前驱结点的前驱结点设置为当前节点的前驱结点。简单理解就是将当前节点的前驱节点设置为第一个找到的正常状态(<=0)的前驱节点
        node.prev = pred = pred.prev;

    // predNext is the apparent node to unsplice. CASes below will
    // fail if not, in which case, we lost race vs another cancel
    // or signal, so no further action is necessary.
    // 获取当前节点的下一个节点
    Node predNext = pred.next;

    // Can use unconditional write instead of CAS here.
    // After this atomic step, other Nodes can skip past us.
    // Before, we are free of interference from other threads.
    // 将当前节点状态设置为1(取消状态)。这里不用CAS的原因是这个执行完其他线程会跳过取消状态,这个执行前无其他线程在执行!
    node.waitStatus = Node.CANCELLED;

    // If we are the tail, remove ourselves.
    // 如果当前节点是尾节点,将尾节点设置为上一个节点。简单理解就是移除当前节点。
    if (node == tail && compareAndSetTail(node, pred)) {
        compareAndSetNext(pred, predNext, null);
    } else {
        // 进入else说明node不是队尾(或者是队尾但是cas队尾失败(其实结果也不是队尾,因为被别的线程抢先了))
        // If successor needs signal, try to set pred's next-link
        // so it will get one. Otherwise wake it up to propagate.
        // 定义一个状态标识
        int ws;
        // 筛选后的前驱节点不是头结点
        // 并且当前节点状态为-1(等待唤醒)或者(当前节点不在运行或者不被取消(<= 0)并且可以将当前节点CAS到-1状态(等待唤醒))
        // 并且前驱节点有线程持有!
        if (pred != head &&
            ((ws = pred.waitStatus) == Node.SIGNAL ||
             (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
            pred.thread != null) {
            // 得到当前节点的下一个节点
            Node next = node.next;
            // 下一个节点不为空,并且下一个节点没有被取消
            if (next != null && next.waitStatus <= 0)
                // CAS将前一个节点与下一个节点连接。简单理解就是跳过(取消)当前节点!
                compareAndSetNext(pred, predNext, next);
        } else {
            // 唤醒下一个不被取消的节点!
            unparkSuccessor(node);
        }
        // 当前节点的下一个节点取消指向
        node.next = node; // help GC
    }
}

唤醒逻辑signalAll()

/**
 * 唤醒全部
 */
public final void signalAll() {
    // AQS在标记运行的线程不是当前线程,抛异常
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    // 获取条件队列的头结点
    Node first = firstWaiter;
    // 条件队列的头结点不是null,尝试去唤醒全部
    if (first != null)
        doSignalAll(first);
}

/**
 * 判断是不是唯一的线程,AQS在标记运行的线程是不是当前线程
 */
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();
}

/**
 * 尝试唤醒全部线程
 */
private void doSignalAll(Node first) {
    // 条件队列的尾结点等于头结点等于null
    lastWaiter = firstWaiter = null;
    // 条件队列(单向列表)出队并进入同步队列(双向列表)
    do {
        // 得到当前节点的下一个节点
        Node next = first.nextWaiter;
        // 设置当前节点的下一个节点为null
        first.nextWaiter = null;
        // 条件队列转同步队列
        transferForSignal(first);
        // 当前节点等于下一个节点
        first = next;
    // 一直循环到下一个节点为null
    } while (first != null);
}

/**
 * 条件队列转同步队列
 */
final boolean transferForSignal(Node node) {
    /*
     * If cannot change waitStatus, the node has been cancelled.
     */
    // 如果无法更改等待状态,则节点已被取消。
    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 p = enq(node);
    // 获取上一个节点的状态
    int ws = p.waitStatus;
    // 节点状态大于0(1:已取消)或者当前节点的状态CAS到-1失败!不能在有需要的时候唤醒,那么现在就唤醒他!为了防止转移到同步队列尾部无法唤醒 
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        // 真正的唤醒!
        LockSupport.unpark(node.thread);
    // 返回条件队列转同步队列成功
    return true;
}

解锁:unlock()方法跟踪

/**
 * unlock他直接调用了sync.release(1)
 */
public void unlock() {
    sync.release(1);
}

/**
 * 释放锁
 */
public final boolean release(int arg) {
    // 尝试释放锁
    if (tryRelease(arg)) {
        // 释放锁成功,获取头结点
        Node h = head;
        // 头结点不为null并且当前节点的状态不在初始化状态
        if (h != null && h.waitStatus != 0)
            // unpark去唤醒队列中的下一个线程
            unparkSuccessor(h);
        // 返回解锁成功
        return true;
    }
    // 返回解锁失败
    return false;
}

/**
 * 尝试解锁
 */
protected final boolean tryRelease(int releases) {
    // 获取当前状态的state值与传入的releases。对应减去前面的重入次数
    int c = getState() - releases;
    // 当前线程不是现在获取锁的线程,抛异常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    // 定义可以释放锁的标记位
    boolean free = false;
    // 回到了初始状态
    if (c == 0) {
        // 释放锁的标记位变为0
        free = true;
        // 设置持有锁的线程为null
        setExclusiveOwnerThread(null);
    }
    // 设置AQS的状态
    setState(c);
    // 返回锁的标记位
    return free;
}

/**
 * unpark去唤醒下一个队列的线程
 */
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;
    // SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
    // CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;
    // PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
    // 对于ReentrantLock,只能出现-1的状态
    if (ws < 0)
        // 通过CAS的方式,设置线程状态为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;
    // 下一个节点为空或者下一个节点的状态大于0(CANCELLED,值为1,表示当前的线程被取消;)
    if (s == null || s.waitStatus > 0) {
        // 下一个节点直接置为null
        s = null;
        // 从尾部开始向前遍历,找到最前的一个处于正常阻塞状态的结点,直到节点重合
        // 从尾部遍历的原因是为了防止在高并发场景下漏掉线程
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    // 过滤后的下一个节点不为null,唤醒他
    if (s != null)
        // 唤醒下一个节点
        LockSupport.unpark(s.thread);
}

结束语

  • 获取更多有价值的文章,让我们一起成为架构师!
  • 关注公众号,可以让你对MySQL有非常深入的了解
  • 关注公众号,每天持续高效的了解并发编程!
  • 关注公众号,后续持续高效的了解spring源码!
  • 这个公众号,无广告!!!每日更新!!!
    作者公众号.jpg
posted @ 2022-01-30 15:06  程序java圈  阅读(86)  评论(0编辑  收藏  举报