Java并发编程小总结:CountDownLatch、CyclicBarrier和Semaphore

Java并发编程小总结:CountDownLatch、CyclicBarrier和Semaphore
这几个类都是在JUC下,也就是java.util.concurrent包下。
这两天学习了一下并发编程中的三个类的使用和一些应用场景,所以做一下记录和总结,方便自己日后再查看复现。

1、CountDownLatch。
这个类的核心思想总结为8个字“秦灭6国,一统华夏”。它可以实现的是一个类似计数器的功能,与CyclicBarrier的思想正好相反。是一个减法操作。
CountDownLatch有且只有一个构造器,且默认提供此构造器,设置初始值的时候必须大于0,否则就会抛异常,可看源码图。

public void await() throws InterruptedException { }; //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行。此方法的位置在真正执行任务之前。
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行。此方法的位置在真正执行任务之前。
public void countDown() { }; //每次线程执行完一次操作后可以将count值减1。如我们例子中灭掉一个国家就执行一次操作--。
public long getCount() {return sync.getCount();} //返回当前计数。


当秦要实现一统华夏的目的,必须把所有的6国都得全部的灭掉之后才可以统一。实行的是一个减法操作。

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {

        CountDownLatch countDownLatch = new CountDownLatch(6);
        long count = countDownLatch.getCount();
        System.out.println(count);
        for (int i = 1; i <= 6; i++) {
            new Thread(() ->{
                System.out.println(Thread.currentThread().getName() + "\t 国,被干掉");
                countDownLatch.countDown();
            }, CountryEnum.forEach_countryEnum(i).getCountry()).start();
        }
        countDownLatch.await();
        System.out.println(" "+ Thread.currentThread().getName() + "\t*************秦帝国,一统华夏");
    }
}

执行结果:

6
齐 国,被干掉
楚 国,被干掉
燕 国,被干掉
秦 国,被干掉
韩 国,被干掉
魏 国,被干掉
main *************秦帝国,一统华夏

2、CyclicBarrier。
这个类的核心思想也总结为8个字“集齐龙珠,召唤神龙”,与CountDownLatch的思想正好相反,是一个加法操作。
CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。
CyclicBarrier的构造器设置的时候初始值也必须大于0。
参数barrierAction为当这些线程都达到barrier状态时会执行的内容。

public int await() throws InterruptedException, BrokenBarrierException { };// 调用await()方法的线程会被挂起,直到所有线程都达到状态时才会执行的任务。
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };//和await()类似,只不过等待一定时间后还有线程未达到状态也不管了,直接执行任务。
有一个reset方法,可以重置计数。还有一些方法暂未被关注。
当要实现召唤神龙的目的,必须把7颗龙珠都集齐才可以执行。实行的是一个加法操作。

public class CyclicBarrierDemo {
    public static void main(String[] args) {

        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,() ->{
            System.out.println("龙珠已经集齐,开始召唤神龙啦******************");
        });
        cyclicBarrier.reset();
        for (int i = 1; i <= 7; i++) {
            int finalI = i;
            new Thread(() ->{
                System.out.println("获得第" + finalI + "颗龙珠");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }

            }).start();
        }
    }
}


执行结果:

获得第1颗龙珠
获得第5颗龙珠
获得第4颗龙珠
获得第3颗龙珠
获得第2颗龙珠
获得第6颗龙珠
获得第7颗龙珠
龙珠已经集齐,开始召唤神龙啦******************

3、Semaphore
这个类翻译过来叫做 信号量 ,可以通俗的理解为一个阻塞队列,只是可以控制同时进入的线程数。通过 acquire() 获取资格,如果没有就等待,当获取到资格执行完任务后可以 release() 退出资格。
Semaphore提供了2个构造器,//参数permits表示许可数目,即同时可以允许多少线程进行访问。//参数fair表示是否是公平的,不会的请自行百度公平和非公平锁。

public void acquire() throws InterruptedException { } //获取一个资格
public void acquire(int permits) throws InterruptedException { } //获取某个指定的资格
public void release() { } //释放当前资格
public void release(int permits) { } //释放指定的资格

这4个方法获取不到都会阻塞,直到获取成功为止。如果想要立刻直到结果可以使用tryAcquire()方法来确认。
public boolean tryAcquire() { }; //尝试获取一个资格,若获取成功,则立即返回true,否则立即返回false
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { }; //尝试获取一个资格,增加了一个超时功能,在指定时间内获得则立即返回true,否则则立即返回false
public boolean tryAcquire(int permits) { }; //尝试获取指定的资格,若获取成功,则立即返回true,否则立即返回false
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { }; //尝试获取指定资格,增加了一个超时功能,若在指定的时间内获取成功则立即返回true,否则则立即返回false
通过availablePermits()方法得到可用的许可数目。

public class SemaphoreDemo {

    public static void main(String[] args) {

        Semaphore semaphore = new Semaphore(5);

        for (int i = 1; i <= 8; i++) {
            int finalI = i;
            new Thread(() ->{
                try {
                    semaphore.acquire();
                    System.out.println(finalI +"--号工人进来使用机器");
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    System.out.println(finalI +"号工人退出使用机器--");
                    semaphore.release();
                }
            }).start();
        }
    }
}


执行结果:

2--号工人进来使用机器
4--号工人进来使用机器
1--号工人进来使用机器
3--号工人进来使用机器
5--号工人进来使用机器
5号工人退出使用机器--
3号工人退出使用机器--
4号工人退出使用机器--
8--号工人进来使用机器
2号工人退出使用机器--
1号工人退出使用机器--
7--号工人进来使用机器
6--号工人进来使用机器
8号工人退出使用机器--
6号工人退出使用机器--
7号工人退出使用机器--

-----------------------------------------------------------------------------------------------------------------------------------------------------

下面对上面说的三个辅助类进行一个总结:

  1)CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:

    CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;

    而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;

    另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。

  2)Semaphore其实和锁有点类似,它一般用于控制对某组资源的访问权限。

这里是参考博主-Matrix海子 大神的case https://www.cnblogs.com/dolphin0520/p/3920397.html
及观看 机构《尚硅谷》阳哥视频,做的小总结。

猜你喜欢

转载自www.cnblogs.com/shijianhenjinpo/p/12222547.html
今日推荐