目前卷文化盛行,为了增强面试能力,开始了无边无际的学习,无边界不是重点,重点是要深入1万米。言归正传,本文打算做一个多线程学习的系列文章,沉淀自我。
前言
本文主要是讲解CyclicBarrier的概念,基本用法,使用场景,底层代码原理剖析。
一、CyclicBarrier是什么?
按照源码注释,翻译成中文,就是以下内容:
- 让一组线程都等待对方达到一个共同的障碍点。
- 循环障碍在程序涉及到固定大小的线程偶尔必须互相等待是有用的。
- 障碍叫做循环,因为在等待线程被释放后它可以重复使用。
- 循环障碍支持一个可选的Runnable命令,在最后一个线程后到达,但是在任何线程被释放前,每运行一次障碍点。 在任何线程继续运行之前,这个动作Runnable更新共享状态是有用的。
- CyclicBarrier对于失败同步尝试使用all-or-none breakage model:如果一个线程离开障碍点过早中断,失败,或超时,所有其他等待障碍点线程也会异常通过离开。
二、使用方法
CyclicBarrier 提供了一些方法:
方法 | 说明 |
---|---|
await() | 使当前线程进入同步队列进行等待,并且值减1 |
await(long timeout, TimeUnit unit) | 带超时时间的await()。 |
getParties() | 返回需要跳过栅栏的线程数量,它就是初始化构造函数的parties值。 |
getNumberWaiting() | 返回目前在栅栏边等待的线程数量,它是一个变化的值。 |
reset() | 重置初始状态的障碍。如果有线程在栅栏边等待会抛出异常BrokenBarrierException |
三、应用场景
主要用于多线程等待,Runnable命令执行后,多线程执行直到释放。
3.1并发分解模式
代码如下
package com.valley.juc.tools.cyclicbarrier;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* @author valley
* @date 2022/6/16
* @Description TODO
*/
public class Solver {
final int N;
final float[][] data;
final CyclicBarrier barrier;
public static void main(String[] args) {
float[][] matrix={{1.2f,1.3f,1.4f,1.5f},{1.2f,1.7f,1.4f,1.5f},{3.2f,1.3f,1.4f,9.5f},{2.2f,2.3f,2.4f,2.5f}};
// float[][] matrix2=new float[][]{{1.2f,1.3f,1.4f,1.5f},{2.2f,2.3f,2.4f,2.5f}};
Solver solver =new Solver(matrix);
}
class Worker implements Runnable {
int myRow;
Worker(int row) {
myRow = row;
}
public void run() {
while (!done()) {
processRow(myRow);
try {
barrier.await();
} catch (InterruptedException ex) {
return;
} catch (BrokenBarrierException ex) {
return;
}finally {
System.out.println("我是"+myRow+"号工人,开始干活。。。");
return;
}
}
}
private void processRow(int myRow) {
System.out.println("我是"+myRow+"号工人,进场了。。。");
}
private boolean done() {
return false;
}
}
public Solver(float[][] matrix) {
data = matrix;
N = matrix.length;
Runnable barrierAction =
new Runnable() {
public void run() {
mergeRows(data);
}
};
barrier = new CyclicBarrier(N, barrierAction);
List<Thread> threads = new ArrayList<Thread>(N);
for (int i = 0; i < N; i++) {
Thread thread = new Thread(new Worker(i));
threads.add(thread);
thread.start();
}
// wait until done
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void mergeRows(float[][] data) {
System.out.println("工人们准备就绪开始干活了");
}
}
四、源码剖析
CyclicBarrier 的源码在JUC并发工具中,相对复杂点:
- 使用ReentrantLock构建lock,也就是构建了栅栏,ReentrantLock底层基于AbstractQueuedSynchronizer 来实现的。
- CyclicBarrier是通过Lock的Condition实现的,每个CyclicBarrier对应个Lock锁和该锁的condition条件。创建CyclicBarrier时设置一个count计数,当调用await()时做两件事:将count-1 ;将线程阻塞并构造成结点加入condition条件队列。当count变为0时,达到等待线程数量要求,condition将条件队列中的线程全部唤醒。
private final ReentrantLock lock = new ReentrantLock();
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
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()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
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();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
- CyclicBarrier 构造函数中指定的count和可选的Runnable命令;
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
public CyclicBarrier(int parties) {
this(parties, null);
}
- await(),等待直到其他线程都到达栅栏,并且每执行一次会将全局变量count减去1直到等于0。
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
/**
* Main barrier code, covering the various policies.
*/
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
int index = --count;
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
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;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
- reset(),加锁操作构建新一代。
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
breakBarrier(); // break the current generation
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
}
五、和其他工具比较
- CountDownLatch与CyclicBarrier
- CountDownLatch和CyclicBarrier都能够实现线程之间的等待,区别如下:
- CountDownLatch一般用于一个或多个线程,等待其他线程执行完任务后再执行,而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;
- CountDownLatch计数减为0后不能重用;而CyclicBarrier可置0后复用。
总结
主要讲解CyclicBarrier,后面重点讲ReentrantLock和AQS。