参考:点击打开链接
CyclicBarrier试用及BrokenBarrierException
CyclicBarrier(int parties, Runnable barrierAction)
通俗的说,它就是一个栅栏,所有线程到了栅栏后必须等待,直到凑够了parties个数量的线程,然后这些线程才可以突破栅栏,继续做事情。否则就等着。
(图片来自参考)
例子:
package threadTest;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class TestCyclicBarrier implements Runnable{
final static int ThreadNUM=10;//线程数
final static int parties=ThreadNUM;//栅栏的parties参数。第二个实验改为6.
//突破栅栏时执行的方法。
static Runnable barrrierAction1=new Runnable(){
public void run(){
System.out.println(Thread.currentThread().getName()+"tripped first barrier");
}
};
static Runnable barrrierAction2=new Runnable(){
public void run(){
System.out.println(Thread.currentThread().getName()+"tripped second barrier");
}
};
static CyclicBarrier barrier1 =new CyclicBarrier(parties,barrrierAction1);
static CyclicBarrier barrier2=new CyclicBarrier(parties,barrrierAction2);
@Override
public void run() {
try {
Random random=new Random();
int time1=1000+random.nextInt(2000);//1000~3000
Thread.sleep(time1);
System.out.println(Thread.currentThread().getName()+":first barrie. num of"+barrier1.getNumberWaiting()+" waiting");
barrier1.await();//第一个栅栏
// barrier1.await(4,TimeUnit.SECONDS);
int time2=1000+random.nextInt(2000);//1000~3000
Thread.sleep(time2);
System.out.println(Thread.currentThread().getName()+":second barrie. num of"+barrier2.getNumberWaiting()+" waiting");
barrier2.await();//第二个栅栏
// barrier1.await(4,TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
for(int i=0;i<ThreadNUM;i++){
TestCyclicBarrier test=new TestCyclicBarrier();
Thread t=new Thread(test);
t.start();
}
}
}
打印:
Thread-8:first barrie. num of0 waiting
Thread-6:first barrie. num of1 waiting
Thread-4:first barrie. num of2 waiting
Thread-0:first barrie. num of3 waiting
Thread-7:first barrie. num of4 waiting
Thread-1:first barrie. num of5 waiting
Thread-9:first barrie. num of6 waiting
Thread-5:first barrie. num of7 waiting
Thread-3:first barrie. num of8 waiting
Thread-2:first barrie. num of9 waiting
Thread-2tripped first barrier
Thread-4:second barrie. num of0 waiting
Thread-3:second barrie. num of1 waiting
Thread-0:second barrie. num of2 waiting
Thread-8:second barrie. num of3 waiting
Thread-1:second barrie. num of4 waiting
Thread-2:second barrie. num of5 waiting
Thread-7:second barrie. num of6 waiting
Thread-6:second barrie. num of6 waiting
Thread-9:second barrie. num of8 waiting
Thread-5:second barrie. num of9 waiting
Thread-5tripped second barrier
存在问题:如果把栅栏的
parties改为6个,程序死在那。

我猜测原因是
CyclicBarrier(int parties)如果 parties小于线程数的话,一部分线程会先突破。后面的被阻挡的线程由于可能凑不够突破数而一直阻塞·,程序无法结束。
把栅栏的parties改成6。有6个线程先突破了。有4个线程没办法突破,因为凑不齐突破条件的6个。
然后发现方法:
int | await(long timeout, TimeUnit unit) |
可以带时间参数。超时抛出TimeoutException。想增加个检测超时的,不让程序死。
把两个
barrier.await()改成
barrier.await(4,TimeUnit.SECONDS); //4秒不能突破的话抛出Timeout异常
运行。然后抛出异常
BrokenBarrierException
If any thread is interrupted while waiting, then all other waiting threads will throw BrokenBarrierException and the barrier is placed in the broken state.
翻译下:某个等待的线程被中断,其它等待的线程会抛出这个异常
1个因为超时中断,3个线程抛出BrokenBarrierException。原因显而易见。
闭锁CountDownLatch试用
就是个计数锁。
CountDownlatch countDownLatch=new CountDownlatch (count);
count代表计数器的大小。当其减少到0时,解除阻塞继续执行代码。
如,一个线程A中
countDownLatch.await();//阻塞
doSomething();
其它线程可以按下计数器,让计数器减1。
countDownLatch.countDown();
然后接着做自己的事情。
线程A的闭锁计数器减少到0时,就解除阻塞,继续做事。
await还有种形式:
boolean await(long timeout, TimeUnit unit)
对于它的返回值
Returns:
true
if the count reached zero and
false
if the waiting time elapsed before the count reached zero
翻译一下
超时的话,如果计数器不为0(
按计数器的线程没达到期望的数量),返回false,解除阻塞。为0的话(
按计数器的
线程达到了期望的数量),返回true。
关于CountDownLatch与CyclicBarrier的区别,这篇文章表达很生动。
个人总结:一个是栅栏,一个是计数锁。
下面是自己写的一个关于CountDownLatch使用的生动小例子。如有雷同,她抄我的。
例子:StudentNum 个学生考试。交卷交晚了和中途弃考的考生会不及格。
对待交卷交完的同学,收卷老师很耐心,不会暴力地抓卷,而是耐心地等她做完交卷,然后给她不及格。有个学生会受神秘力量影响,神经错乱大闹考场(被中断了)。但是大家精力都很集中,不会受他的影响而中断考试。
package threadTest;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class TestCountDownLatch {
final static int StudentNum = 20;// 线程数
static volatile boolean isInExam = false;// 是否在考试时间内
static Map<String, Long> timeUse = new ConcurrentHashMap<>();// 记录交卷时间
static Set<String> lateSet = new CopyOnWriteArraySet<String>();// 交卷交晚了的学生
public static void main(String[] args) {
TestCountDownLatch test = new TestCountDownLatch();
// 耐心的收卷老师。会等所有考生提交了卷子,而不是抓卷。
CountDownLatch teacher = new CountDownLatch(StudentNum);
// 考试开始的信号,不准提前写卷子。
CountDownLatch startExamSignal = new CountDownLatch(1);
// 考试结束的信号,没按时间交卷的同学会被阴险的老师记到lateSet。如果都交卷了,考试提前结束。
CountDownLatch endExamSignal = new CountDownLatch(StudentNum);
Thread[] threadList = new Thread[StudentNum];
// 考生进场
for (int i = 0; i < StudentNum; i++) {
threadList[i] = new Thread(test.new Student(
Integer.toString(10000 + i), startExamSignal, endExamSignal,teacher));
threadList[i].start();
}
// 好像有考生今天不太对劲
Random random = new Random();
new Thread(test.new UnexpectedEvent(
threadList[random.nextInt(StudentNum - 1)], startExamSignal))
.start();
isInExam = true;
// 考试开始!
System.out.println("老师: 考试开始!");
startExamSignal.countDown();
// 老师无聊地监考
try {
isInExam = endExamSignal.await(2, TimeUnit.SECONDS);// 考试时间2秒
System.out.println("老师:考试结束了!!不要写了!!");
//老师收拾好了所有卷子
teacher.await();
System.out.println("老师:下面宣布中途弃考和没按时交卷的考生!!直接不及格!!");
System.out.println(lateSet);
} catch (InterruptedException e) {
System.out.println("考试居然中断了!!!");
e.printStackTrace();
}
}
class Student implements Runnable {
private String testNum;// 考号
private CountDownLatch startExamSignal;// 考试开始的信号
private CountDownLatch endExamSignal;// 考试结束的信号
private CountDownLatch teacher;// 收卷老师
public Student(String testNum, CountDownLatch startExamSignal,
CountDownLatch endExamSignal,CountDownLatch teacher) {
this.testNum = testNum;
this.startExamSignal = startExamSignal;
this.endExamSignal=endExamSignal;
this.teacher = teacher;
}
@Override
public void run() {
Random random = new Random();
int time1 = 1000 + random.nextInt(2000);// 1000~3000
long startTime=0;
try {
startExamSignal.await();// 不准提前答卷。
startTime=System.currentTimeMillis();
Thread.sleep(time1);// 写卷子
// System.out.println(teacher.getCount());
} catch (InterruptedException e) {
System.out.printf(
"学生%s:电子竞技处于水深火热中!劳资不考了!" + System.lineSeparator(),testNum);
//监考人员把他记到不合格名单中。
lateSet.add(testNum);
e.printStackTrace();
} finally {
timeUse.put(testNum,System.currentTimeMillis()-startTime);
//交卷交晚的,记到不合格名单中。
if (!isInExam) {
lateSet.add(testNum);
}
System.out.printf("考生%s提交了卷子" + System.lineSeparator(), testNum);
teacher.countDown();// 交卷给收卷老师
endExamSignal.countDown();
}
}
}
// 神秘力量挑选一个学生让他神经错乱(中断)
class UnexpectedEvent implements Runnable {
private Thread unfortunatelyStudent;
private CountDownLatch startExamSignal;// 考试开始的信号
UnexpectedEvent(Thread unfortunatelyStudent,
CountDownLatch startExamSignal) {
this.unfortunatelyStudent = unfortunatelyStudent;
this.startExamSignal = startExamSignal;
}
@Override
public void run() {
try {
Random random = new Random();
long timeStart = 1200 + random.nextInt(400);
startExamSignal.await();
long time = System.currentTimeMillis();
while (System.currentTimeMillis() - time < timeStart) {
}
//中断一个线程
unfortunatelyStudent.interrupt();
} catch (InterruptedException e) {
System.out.println("就当啥事都没发生过");
e.printStackTrace();
}
}
}
}
打印:
