并发面试题:CountDownLatch、CyclicBarri
23. CountDownLatch****类
CountDownLatch类似计数器的功能,CountDownLatch是一种灵活的闭锁实现,能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成了任务,然后在CountDownLatch上等待的线程就可以恢复执行任务。
需求1:计算10个学生抢苹果的总时间。
问题代码:
public class NoCountDownLatchDemo {
public static voidmain(String[] args) throws InterruptedException {
long begin = System.currentTimeMillis();
Apple apple= new Apple();
for (int i= 0; i< 10; i++) {
new Thread(apple).start();
}
long end = System.currentTimeMillis();
System.out.println("共耗费" + (end- begin) + "ms时间");
}
}
classApple implements Runnable {
private int appleNum = 100;
@Override
public void run() {
for (int i= 0; i< 1000; i++) {
synchronized (this) {
if (appleNum > 0) {
System.out.println(Thread.currentThread().getName() + "抢到第"+ appleNum-- + "个苹果");
}
}
}
}
}
测试结果:
[图片上传失败...(image-c7bb34-1548232986922)]
问题分析:
耗费总时间应该是苹果抢完之后才计算出来的,现在还在抢的过程中就计算出来了,所以我们需要在计算消耗时间之前设置一个关卡,判断抢苹果线程是否都结束,如果都结束了就执行时间的计算。
分析如图所示:
[图片上传失败...(image-332aef-1548232986922)]
正确代码:
public class CountDownLatchDemo {
public static voidmain(String[] args) throws InterruptedException {
CountDownLatch latch= new CountDownLatch(10);
long begin = System.currentTimeMillis();
Apple1 apple= new Apple1(latch);
for (int i= 0; i< 10; i++) {
new Thread(apple).start();
}
latch.await();//不让main线程往下运行,直到吃苹果线程都运行完毕,才放行
long end = System.currentTimeMillis();
System.out.println("共耗费"+(end-begin)+"ms时间");
}
}
classApple1 implements Runnable{
private int appleNum = 100;
privateCountDownLatch latch;
publicApple1(CountDownLatch latch) {
this.latch= latch;
}
@Override
public void run() {
try {
for (int i= 0; i< 1000; i++) {
synchronized (this) {
if (appleNum>0) {
System.out.println(Thread.currentThread().getName()+"抢到第"+appleNum--+"个苹果");
}
}
}
} finally {//必须要执行
latch.countDown();
}
}
}
24. CyclicBarrier****类
CyclicBarrier回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。我们暂且把这个状态就叫做barrier,当调用await()方法之后,线程就处于barrier了。
需求:通知10个线程开会,线程到达会议室的时间是不一致的,人齐开会。
代码演示:
public class CyclicBarrierDemo {
public static voidmain(String[] args) {
CyclicBarrier barrier= newCyclicBarrier(10, new Runnable() {
@Override
public void run() {
System.out.println("人已经到齐,准备开会!");
}
});
for (int i= 0; i< 10; i++) {
new Thread(new Meet(barrier)).start();
}
}
}
classMeet implements Runnable {
privateCyclicBarrier barrier;
publicMeet(CyclicBarrier barrier) {
this.barrier= barrier;
}
public void meeting() {
System.out.println(Thread.currentThread().getName() + "到达会议室等待");
try {
barrier.await();//中间设置了一道屏障
} catch(Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "开会中!");
}
@Override
public void run() {
meeting();
}
}
25. Semaphore****类
Semaphore英文含义为信号量,Semaphore可以控制同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。
需求:模拟车子等红路灯,绿灯表示线程获取许可哦,通过马路,但是是一批一批的过马路,过马路的车辆数量是固定的,最多不能超过这个数。
代码演示:
public class SemaphoreDemo {
public static voidmain(String[] args) {
Semaphore semaphore= new Semaphore(10);
for (int i= 0; i< 100; i++) {
new Thread(new Car(semaphore)).start();
}
}
}
classCar implements Runnable{
privateSemaphore semaphore;
publicCar(Semaphore semaphore) {
super();
this.semaphore= semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();//绿灯,获得许可证
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName()+"-->"+"一批一批过马路");
semaphore.release();//许可证发完,转红灯
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
26. CountDownLatch****、****CyclicBarrie****、****Semaphorer****三者的区别
Ø CountDownLatch:使一个线程A或是组线程A等待其它线程执行完毕后,一个线程A或是组线程A才继续执行(发现参差不齐,等一等,重新在起步)。CyclicBarrier:等所有的线程到达一个点,中间做完barrierAction的事情之后,才能重新各自做各自的事情。(人齐来会,开会后,各自完成各自任务)Semaphore:一批一批线程执行
Ø CountDownLatch是减计数方式,计数==0时释放所有等待的线程;CyclicBarrier是加计数方式,计数达到指定的值时释放所有等待的线程。Semaphore调用semaphore.acquire()得到一个许可,当许可到了一定的数量时可以开始执行线程.
Ø CountDownLatch当计数到0时,计数无法被重置;CyclicBarrier计数达到指定值时,此时计数设置为0,又可以重新开始。
Ø CountDownLatch每次调用countDown()方法计数减一,调用await()方法只进行阻塞,对计数没任何影响;CyclicBarrier只有一个await()方法,调用await()方法计数加1,若加1后的值不等于构造方法的值,则线程阻塞。
Ø CountDownLatch、CyclikBarrier这个值作为计数用,达到该次数即释放等待的线程,而Semaphore 中所有acquire获取到的资源达到这个数,会使得其它线程阻塞。