java线程栅栏_Java 多线程基础 - CyclicBarrier

本文介绍了Java并发包中CyclicBarrier类的基本概念与使用方法,并通过示例代码展示了如何利用CyclicBarrier来同步多个线程,确保所有参与线程达到同步点后继续执行,适用于多线程任务最终汇总操作的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我的博客 转载请注明原创出处。

java.util.concurrent包里有几个能帮助人们管理相互合作的线程集的类,为多线程常见的应用场景预置了抽象好的类库。在遇到这些应用场景时应该直接重用合适的库类而不要试图提供手工的锁与条件的集合。

e7feac2950d139d2304bc1c36cd67927.png

同步屏障 CyclicBarrier

官方定义上文已经给出,人话版是等待特定数量的线程都到达同步屏障后各线程才继续执行。

2dde2ea284b193c9e6389e6964cd997f.png

同步屏障有两个构造函数,第一个构造函数只需要指定需要等待的线程数量,第二构造函数多了一个在特定数量的线程都到达同步屏障时优先执行的Runnable。

例子:

public class CyclicBarrierTest {

// 等待4个线程到达同步屏障,全部到达后优先执行一个 Runnable

private static CyclicBarrier cyclicBarrier = new CyclicBarrier(4,

() -> System.out.println("全部到达同步屏障" + LocalDateTime.now()));

public static void main(String[] args) throws InterruptedException, BrokenBarrierException {

Runnable runnable = () -> {

System.out.println("到达同步屏障" + LocalDateTime.now());

try {

cyclicBarrier.await();

} catch (InterruptedException | BrokenBarrierException e) {

e.printStackTrace();

}

System.out.println("继续执行");

};

List list = Arrays.asList(runnable, runnable, runnable);

list.forEach(runnable1 -> new Thread(runnable1).start());

Thread.sleep(1000);

System.out.println("最后一个线程到达同步屏障");

cyclicBarrier.await();

}

}

输出:

到达同步屏障2018-08-12T14:33:16.769

到达同步屏障2018-08-12T14:33:16.769

到达同步屏障2018-08-12T14:33:16.769

最后一个线程到达同步屏障

全部到达同步屏障2018-08-12T14:33:17.746

继续执行

继续执行

继续执行

Process finished with exit code 0

同步屏障的应用场景是那种多线程执行任务,在全部任务执行完成后需要进行一些操作的场景。比如对每个用户进行充值统计,最后汇总返回。

c2503baede2c9452a639d1102df9ef32.png

CyclicBarrier的方法如上,分别是

getParties() 返回需要到达同步屏障的线程数量

await() 等待所有线程到达

await(long, TimeUnit) 带时间限制的await()

isBroken() 判断阻塞的线程是否被中断

reset() 重置计数器

getNumberWaiting() 当前被阻塞的线程数量,该方法主要用于调试和断言

源码分析

那么CyclicBarrier是怎么实现这个效果的呢?我们从最常用的await()方法入手。

9225dac372ebbfbc0f3eced4273076d7.png

可以看到await()方法主要是调用了CyclicBarrier私有的dowait()方法

b4db7053a3a88e930c083fbc166706fb.png

如注释所言,dowait()方法就是实现功能的主要方法了。

d3ad754dbbb765fe13dbadd2f769bc4b.png

首先拿到可重入的锁

69a69a9d9e61e5be477d1052f2eaeb35.png

06cf833192553b466ac4cd8e5cf38bb8.png

be8a9ba0de3fadf84c879acc328d26af.png

然后通过内部类Generation判断阻塞的线程是否被中断或该屏障已经失效。

64c8b97f11ba60dcc392232f224a33f8.png

4425b418a1799254bb9c13ac88d38972.png

如果线程没有被中断,那么就获取还没有到达的线程数量并减一。如果已经没有需要等待的线程了,就判断是否有需要执行的Runnable。如果没报错就更新屏障状态并唤醒所有线程继续执行。Runnable执行报错的话执行breakBarrier()方法。

4f547cb846a1fab60a2c27bf0f8b15e0.png

如果还有未到达的线程,就进入一个死循环,直到超时、线程中断、屏障失效、全部完成等情况下退出。

完整的代码:

/**

* Each use of the barrier is represented as a generation instance.

* The generation changes whenever the barrier is tripped, or

* is reset. There can be many generations associated with threads

* using the barrier - due to the non-deterministic way the lock

* may be allocated to waiting threads - but only one of these

* can be active at a time (the one to which {@code count} applies)

* and all the rest are either broken or tripped.

* There need not be an active generation if there has been a break

* but no subsequent reset.

*/

private static class Generation {

boolean broken = false;

}

/** The lock for guarding barrier entry */

private final ReentrantLock lock = new ReentrantLock();

/** Condition to wait on until tripped */

private final Condition trip = lock.newCondition();

/** The number of parties */

private final int parties;

/* The command to run when tripped */

private final Runnable barrierCommand;

/** The current generation */

private Generation generation = new Generation();

/**

* Number of parties still waiting. Counts down from parties to 0

* on each generation. It is reset to parties on each new

* generation or when broken.

*/

private int count;

/**

* Updates state on barrier trip and wakes up everyone.

* Called only while holding lock.

*/

private void nextGeneration() {

// signal completion of last generation

trip.signalAll();

// set up next generation

count = parties;

generation = new Generation();

}

/**

* Sets current barrier generation as broken and wakes up everyone.

* Called only while holding lock.

*/

private void breakBarrier() {

generation.broken = true;

count = parties;

trip.signalAll();

}

/**

* 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();

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值