并发面试题:CountDownLatch、CyclicBarri

2020-04-27  本文已影响0人  Petrel_Huang

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获取到的资源达到这个数,会使得其它线程阻塞。

上一篇 下一篇

猜你喜欢

热点阅读