目的:お互いのために待機中のスレッドのセットが共通のバリアポイントに到達できるようにします。
循環障壁は時折お互いのために待機中のスレッドの固定セットを達成することができます。スレッドを待って、それを再利用できる解放されるので、この障壁は、サイクルと呼ばれています。
それはすべてのスレッド、到着時に最後のスレッドグループ内のバリアポイントリリースのスレッドに到達する前にタスクを完了するために、各スレッドのループは、スレッドの実行コマンドいったんバリアポイントバリアをサポートしています。メタファー:新入生や二年生、三年生かどうか。私が署名する最初になるように設定し、彼らは誰もが遊び場の収集を完了するのを待ち、その後、エクササイズをやって起動することができます。そして、各学生のためのこのプロセスは、バリア点として理解されるべきでサインオンします。一度に必要なすべての人、学生が後に到着する前に、誰もが看板を行います最後の記号は、出席した後、誰もが一緒に運動を行います。
学生は、異常事態(中断)に落ち、熱中症(異常)、運動をしながら、他の学生が最初にしないように、BrokenBarrierExceptionまたはInterruptedExceptionあるを通じて障壁がループを(タイムアウト)が来ていないが存在することになりましたと。一貫性を維持します。
たびに障壁は、インスタンス一度生成すると考えられます。バリアがトリガされるまたはリセットされているすべての時間は、変更されます。これはまた、この障壁を使用した場合、スレッドに関連付けられている事例があるでしょう、多くの世代につながりました。このため、不確実性の方法は、このロックを待機中のスレッドに割り当てることができます(ただし、1つのみが一度にアクティブにできる)と、中断またはトリガされたいずれかのスレッドのすべての残りの部分。あなたはすでに、割り込みが、遅くともリセット、ここではアクティブのインスタンスを持っている場合。
シンプル上の段落を理解するために:スレッドのグループを同時に処理する際の障壁が唯一のインスタンスをリセットする必要があり、循環障壁を達成したい後に排出、他のスレッド破るまたはトリガのいずれかで、同時に複数のスレッドを実行し、一度使用することができます。
ソース決意
package java.util.concurrent;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
JDK版本1.5出现 类似CountDownLatch
public class CyclicBarrier {
private static class Generation {
boolean broken = false;
}
用来保证屏障入口的锁
private final ReentrantLock lock = new ReentrantLock();
条件信号量,条件等待直到触发
private final Condition trip = lock.newCondition();
固定的线程数
private final int parties;
当触发的时候这个命令运行
private final Runnable barrierCommand;
当前的实例
private Generation generation = new Generation();
仍然等待的线程数,从固定的线程数根据每个实例执行依次递减为0.
每重新生成一个实例或者中断一个线程,它都会被重置数量
private int count;
更新屏障触发的状态,并且通知所有其他线程
仅仅当持有锁的时候被调用
private void nextGeneration() {
// 上一个实例完成的信号
trip.signalAll();
// 设置下一个实例
count = parties;
generation = new Generation();
}
将当前线程设置为中断链接,并通知所有其他线程
仅仅当持有锁的时候被调用
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}
主要的屏障代码,覆盖了实现屏障的各种策略
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();
}
组内线程数递减1
int index = --count;
如果下标为0
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();
}
}
// 循环直到被触发,中止,中断,或者超时
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 {
即使没有被中断,我们打算完成等待,因此这里属于后续操作
Thread.currentThread().interrupt();
}
}
如果被断开链接,抛出BrokenBarrierException
if (g.broken)
throw new BrokenBarrierException();
如果代在中途被修改了,返回下标
if (g != generation)
return index;
如果超时了,中断屏障,抛出超时异常
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
释放持有的锁
lock.unlock();
}
}
创建一个新的循环屏障,用于触发设置的这个组内的线程,并且将会执行这个被给定的屏障行为barrierAction,当这个屏障被触发时候,由进入屏障的最后一个线程执行
public CyclicBarrier(int parties, Runnable barrierAction) {
如果组内线程数parties小于1,抛出非法参数异常
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
如果没有任何操作则为null,如果屏障点被触发,这个命令执行
this.barrierCommand = barrierAction;
}
创建一个新的循环屏障,用于触发设置的这个组内的线程,但是在触发的时候不执行任何预定义操作,其实就是上面的方法,没有指定线程执行的行为
public CyclicBarrier(int parties) {
this(parties, null);
}
返回这个屏障点卡着的线程数量
public int getParties() {
return parties;
}
直到所有的屏障点卡着的线程数量(上面的那个方法)都已经执行await方法在这个屏障点。
如果当前线程不是最后到达的,那么他将禁止所有线程调度并且休眠,直到下列条件有一种情况的发生:
@1、最后的一个线程到达,或者一些其他线程中断当前线程
@2、一些其他线程中断其他等待线程
@3、一些线程超时当等待屏障的时候
@4、一些线程在这个屏障点执行了重置
如果这个当前线程有他中断的状态设置在进入到这个方法。或者是中断当等待的时候,那么中断异常会被throw出来并且当前线程的中断状态被清除掉。
如果这个屏障被重置当其他线程等待的时候,或者这个屏障被中断链接,当执行await方法的时候,或者当其他线程正在等待,那么BrokenBarrierException被抛出
如果当等待的时候,如果任意线程被中断,那么其他所有的等待线程都会抛出BrokenBarrierException异常,并且这个屏障被中断状态所替代。
如果当前线程是最后到达的线程,和构造函数中提供了非null barrier操作,那么这个当前线程在允许其他线程继续之前运行这个操作。
如果一个异常在屏障行为执行期间发生,那么这个异常将在当前线程传递,并且这个屏障会被中断状态所替代。
public int await() throws InterruptedException, BrokenBarrierException {
try {
当前线程的到达索引,其中索引{@code getParties() - 1}表示第一个到达,0表示最后到达
return dowait(false, 0L);
} catch (TimeoutException toe) {
不可能发生
throw new Error(toe);
}
}
与上面类似,只是增加了时间限制。指定的等待时间已经过了
timeout:等待屏障的时间,unit超时参数的时间单位
public int await(long timeout, TimeUnit unit)
throws InterruptedException,
BrokenBarrierException,
TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
查询这个屏障是否是中断状态
public boolean isBroken() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return generation.broken;
} finally {
lock.unlock();
}
}
重置这个屏障的初始状态,如果其他组线程是当前等待在这个屏障,他们将返回一个BrokenBarrierException异常。需要注意的是:在由于其他可能很复杂原因,已经发生异常的情况下可能重新建一个比重置好的多。
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
中断当前的代实例
breakBarrier();
开启一个新的代实例
nextGeneration();
} finally {
lock.unlock();
}
}
返回正在屏障等待的线程数量,这个方法主要用于debug的时候和断言的时候
public int getNumberWaiting() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return parties - count;
} finally {
lock.unlock();
}
}
}
ケース実現
package com.xiaochengxinyizhan.interview.threadcoding.lock;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String args[]) throws InterruptedException{
final float[][] data = new float[][]{{1,2},{2,3},{3,4},{4,5}};
Solver solver=new Solver(data);
}
}
class Solver {
//数据值
final int N;
//需要计算的数组
final float[][] data;
//循环屏障
final CyclicBarrier barrier;
//创建一个值接受所有数据最终结果值
ConcurrentHashMap<Float,Float> reslut = new ConcurrentHashMap<Float,Float>();
//工人
class Worker implements Runnable {
int myRow;
Worker(int row) {
myRow = row;
}
public void run() {
try {
float count1 =0;
float [] floats=data[myRow];
for (float count:floats){
count1+=count;
}
reslut.put(Float.valueOf(myRow),count1);
barrier.await();
} catch (InterruptedException ex) {
return;
} catch (BrokenBarrierException ex) {
return;
}
}
}
public Solver(float[][] matrix) throws InterruptedException{
//需要计算的数组值
data = matrix;
//由于worker只计算单行内容,所以数组的长度就是需要执行的线程的个数
N = matrix.length;
//屏障执行命令 最后一个到达的时候
Runnable barrierAction =
new Runnable() {
@Override
public void run() {
float conumMerge=0;
// mergeRows(...);
Enumeration em=reslut.keys();
while(em.hasMoreElements()){
conumMerge += reslut.get(em.nextElement());
}
System.out.println("总和:{}"+conumMerge);
}
};
//规定屏障固定线程数和最后一个线程到达后执行的命令行为
barrier = new CyclicBarrier(N, barrierAction);
//创建线程个数list集合
// List<Thread> threads = new ArrayList<Thread>(N);
//遍历线程个数执行线程
for (int i = 0; i < N; i++) {
Thread thread = new Thread(new Worker(i));
//将所有执行的线程worker都放到线程个数list集合里面
// threads.add(thread);
//启动线程
thread.start();
}
// wait until done
// for (Thread thread : threads){
// thread.join();
// }
}
}
要約:
アトミックは、カウンタを更新し、キューの状態変数は、排他ロックReentrantLockのによって達成されるスレッドの同期を使用して達成します。
循環バリア原則は:スレッドのセットは、すべての前にすべてを一度に、それはすべての待機中のスレッドが終了したときに再利用できるので、理由はループと呼ばれている状態を達成しようと、リセット状態CyclicBarrierをした後、その理由は、バリアと呼ばれていますスレッドの呼び出しは、メソッドを待つためにブロックされます、このポイントは、ポイントを遮断するバリアと呼ばれ、そのすべてのスレッドがのawaitメソッドを呼び出し、彼らはスレッド障壁の外になり、ダウンを実行し続けました。
だから何されたCountDownLatchそれを実現していませんか?スレッドはステップ3、B 3ステップのスレッドの実装を実現するものとします。各ステップとBのスレッドの同期。ここでの締約国は、スレッドの数を記録するために使用され、どのように多くのスレッドが待つ呼び出した後、それはここで言った、すべてのスレッドが障壁を打破していきます。スレッドの呼び出しは、メソッドデクリメントを待つたびカウント値が0に等しい場合と、最初等しいパーティーを意味数えるそのバリアポイントへのすべてのスレッド。CycleBarierを再利用できるので、なぜ、価値を維持していない、2つの変数を使用する理由は、常にレコードを通すために使用される関係者の総数は、カウンタのカウント値がゼロになった場合、当事者は値が割り当てられますそれによって多重化を行う、COUNTするために、2つの引数は、この構成では、オブジェクトCyclicBarrierを時間を通過しています。
barriercommandは、このタスクのタスク実行タイミングでコンストラクタ、を通過するすべてのスレッドがバリアポイントに達したとき、更新カウンタのカウントのアトミック性を確保する最初のロックを使用しています。また、スレッド間の同期化の使用をサポートするためのロック条件変数の旅の使用が操作を待って信号。
バリアを突破されているかどうかを、ここでは破壊され、揮発性として宣言されていない、ロックでの変数の使用があるとして、そう宣言する必要はありません。