一个cyclicbarrier让一堆的线程处于等待状态,它们彼此之间会有相同的阻碍点。这个阻碍是循环的,因为当等待的线程等到释放且,它可能会被重复利用。这个同步器在应用的程序中是非常有用的,它会执行一个固定大小的线程,偶尔必须等待每一个线程。
java.util.concurrent.CyclicBarrier类继续一个循环的阻碍同步器。你可以通过执行这个类的CyclicBarrier(int parties)的构造器去明确实例化一个CyclicBarrier的实例。当parties的值小于1时,这个构造器将会抛出IllegalArgumentException。
实际上你也执行CyclicBarrier(int parties, Runnabl barrierAction)的构造器去初始化一个循环的阻碍部分;其中的当阻碍被执行时,barrierAction将会执行。换句话说,parties -1个线程在等待,当多个线程到达时,到达的线程将会执行barrierAction和所有线程都会向前执行。这个runnable在所有线程都在执行时,而去更新分享的状态是非常的用的。当parties的值小于1时,就会抛出IllegalArgument的错误。(在前面的构造器执行这个构造器时,如果放入的barrierAction为null,那么当阻碍释放后,将没的线程会被执行。)
CyclicBarrier也提供了如下的方法:
1) int await():强迫正在请求的线程处于等待状态,直到所有部分在cyclic barrier中都执行await()的方法。当它或其它等待线程被打断,或其它线程在等待时超时,或在这个cyclic barrier中执行reset()的方法,那么在请求的线程会停止等待。如果正在请求的线程自己在进行执行时被打断,或在等待中被打断,这个方法将会抛出InterruptedException,并且请求的线程打断状态会被清空。任何线程在等待时被执行reset()的方法,或执行await()时被打断,或任何等待线程被打断,那么将会抛出BrokenBarrierException.当在等待时被打断,或者等待的线程抛出BrokenBarrierException,那么障碍(barrier)都会处于打断状态。如果请求的线程是最后一个到达,和构造器的barrierAction不为null,在允许其它线程继续执行前,将会让请求的线程去执行。这个方法将会返回请求线程的索引值,这里的索引getParties()-1说明从第一个线程到达到最后一个线程的达到。
2) int await(long timeout,TimeUnit unit):这个方法与前面的方法是相似的,唯一不一样的是,它让你明确要多长时间让请求的线程进入到等待状态。在线程等待期间而超时了,那么将会抛出java.util.concurrent.TimeoutException.
3) int getNumberWaiting():返回当前阻碍下等待的线程数量。这个方法对于打debug和输出信息很有用。
4) int getParties():返回需要调用障碍(barrier)的数量。
5) boolean isBroken():当一个或更多个部分被这个barrier打断,因为打断、超时、重置,或因为异常的抛出错误,这个方法将会返回true,其它情况返回false.
6) void reset():重置这个障碍(barrier)去初始化它的状态。如果任何在障碍(barrier)中等待,那么将会抛出BrokenBarrierException.
Cyclicbarriers在平形分隔的情况是很有用的。当一个长任务被分解为多个子任务,然后将子任务执行的结果再合并成一个结果返回。如下的Listing6-2的例子。
Listing6-2
package com.owen.thread.chapter6;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo
{
public static void main(String[] args)
{
float[][] matrix = new float[3][3];
int counter = 0;
for (int row = 0; row < matrix.length; row++)
for (int col = 0; col < matrix[0].length; col++)
matrix[row][col] = counter++;
dump(matrix);
System.out.println();
Solver solver = new Solver(matrix);
System.out.println();
dump(matrix);
}
static void dump(float[][] matrix)
{
for (int row = 0; row < matrix.length; row++)
{
for (int col = 0; col < matrix[0].length; col++)
System.out.print(matrix[row][col] + " ");
System.out.println();
}
}
}
class Solver
{
final int N;
final float[][] data;
final CyclicBarrier barrier;
class Worker implements Runnable
{
int myRow;
boolean done = false;
Worker(int row)
{
myRow = row;
}
boolean done()
{
return done;
}
void processRow(int myRow)
{
System.out.println("Processing row: " + myRow);
for (int i = 0; i < N; i++)
data[myRow][i] *= 10;
done = true;
}
@Override
public void run()
{
while (!done())
{
processRow(myRow);
try
{
barrier.await();
} catch (InterruptedException ie)
{
return;
} catch (BrokenBarrierException bbe)
{
return;
}
}
}
}
public Solver(float[][] matrix)
{
data = matrix;
N = matrix.length;
barrier = new CyclicBarrier(N, new Runnable()
{
@Override
public void run()
{
mergeRows();
}
});
for (int i = 0; i < N; ++i)
new Thread(new Worker(i)).start();
waitUntilDone();
}
void mergeRows()
{
System.out.println("merging");
synchronized ("abc")
{
"abc".notify();
}
}
void waitUntilDone()
{
synchronized ("abc")
{
try
{
System.out.println("main thread waiting");
"abc".wait();
System.out.println("main thread notified");
} catch (InterruptedException ie)
{
System.out.println("main thread interrupted");
}
}
}
}
首先主线程创建一个浮点型的数组,然后通过dump的方法,将创建的数组值输出。然后线程会去实例化Solver的对象,这里会分隔线程去计算每一行的值。也就是说修改原告数组内的值,只是按行分隔修改。
Solver出现一个构造器,这个构造器中会去接收数组的值的,并且保存它的引用,当然还就是定义一个参数N去保存数组的大小。构造器创建一个有N部分和一个障碍(barrier)的执行动作的cyclic barrier。在障碍(barrier)的执行中,是将数组计算的所有行的结果合并放入到数组中的。最后,构造器创建一个工作的线程,这个工作线程会被切分多个工作执行,就是去修改每一行的矩阵值。构造器会处于等待状,直到所有的工作都完成。
Worker会重复地去执行run()方法,执行run()方法会去执行processRow()的方法,这个方法会明确去修改矩阵行的值,直到done()返回true的值,方法才会停止执行。当明确processRow()的方法执行完了,那么就会执行await()的方法,线程就不能继续前进。
最为关键的是,所有工作线程都会执行await()的方法。当最后一个工作线程执行好了矩阵的修改后,就会执行await()的方法,然后触发障碍(barrier)的动作,使用刚刚执行的所有工作线程合并成一个矩阵的结果。在这个例子是,合并不是必须的,但是在更为复杂的例子中,合并是必须的。
最后任务执行mergeRows()的方法,去唤醒主线程执行Solver的构造器。这个线程连接着一个字符串“abc”对象,使线程处于等待状态。调用notify()的方法,仅仅是唤醒在这个监听器中的等待线程。
执行上面的代码,你可能会得到相似的结果:
0.0 1.0 2.0
3.0 4.0 5.0
6.0 7.0 8.0
Processing row: 0
main thread waiting
Processing row: 1
Processing row: 2
merging
main thread notified
0.0 10.0 20.0
30.0 40.0 50.0
60.0 70.0 80.0