• 常用辅助类(ContDownLatch、CyclicBarrier、Semaphore)


    1. ContDownLatch(倒计时器)

    ContDownLatch是一个同步辅助类,在完成某些运算时,只有其他所有线程的运算全部完成,当前运算才继续执行,这就叫闭锁。

    ● CountDownLatch(int count):实例化一个倒计数器,count指定计数个数

    ● countDown():计数减一

    ● await():等待,当计数减到0时,所有线程并行执行

    代码演示:火箭发射

    public class ConutDownLatchDemo {
        public static void main(String[] args) throws InterruptedException {
    
            // 倒计时器:计数数量为10
            CountDownLatch latch = new CountDownLatch(10);        // 倒计时检查任务,使用10个线程来完成任务
            CountRunnable runnable = new CountRunnable(latch);
            for (int i = 0; i < 10; i++) {
                new Thread(runnable).start(); // 执行任务
            }
            // 等待检查完成
            latch.await();
            // 发射火箭
            System.out.println("Fire!");
        }
    }
    
    class CountRunnable implements Runnable {
    
        private CountDownLatch countDownLatch;
    
        public CountRunnable(CountDownLatch countDownLatch) {
            this.countDownLatch = countDownLatch;
        }
    
        @Override
        public void run() {
            // 模拟检查任务
            try {
                Thread.sleep(new Random().nextInt(10) * 1000);
                System.out.println("check complete");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // 计数 -1
                // 放在finally避免任务执行过程出现异常,导致countDown()不能被执行
                countDownLatch.countDown();
            }
        }
    }

    上述代码中我们先生成了一个CountDownLatch实例。计数数量为10,这表示需要有10个线程来完成任务,等待在CountDownLatch上的线程才能继续执行。latch.countDown();方法作用是通知CountDownLatch有一个线程已经准备完毕,倒计数器可以减一了。latch.await()方法要求主线程等待所有10个检查任务全部准备好才一起并行执行。

    2. CyclicBarrier(加法计数器)

    一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。

    使用场景

    需要所有的子任务都完成时,才执行主任务,这个时候就可以选择使用CyclicBarrier。

    代码演示:集龙珠召唤神龙

    public class CyclicBarrierDemo {
        public static void main(String[] args) {
    
            // 加法计数器,集齐7颗龙珠召唤神龙
            CyclicBarrier cyclicBarrier = new CyclicBarrier(7, new Runnable() {
                @Override
                public void run() {
                    System.out.println("集齐龙珠 -> 召唤神龙!嗷!嗷!嗷!!!");
                }
            });
    
            for (int i = 0; i < 7; i++) {
                new Thread(() -> {
                    System.out.println("获得(" + Thread.currentThread().getName() + ")星龙珠");
                    try {
                        cyclicBarrier.await(); // 等待其它线程执行完成
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }, String.valueOf(i + 1)).start();
            }
        }
    }

    ◇  CountDownLatch 和 CyclicBarrier 比较

    ① CountDownLatch 是线程组之间的等待,即一个(或多个)线程等待N个线程完成某件事情之后再执行。

      CyclicBarrier 则是线程组内的等待,即每个线程相互等待,即 N 个线程都被拦截之后,然后依次执行。

    ② CountDownLatch 是减计数方式。

      CyclicBarrier 是加计数方式。

    ③ CountDownLatch 计数为 0 无法重置。

      CyclicBarrier 计数达到初始值,则可以重置。

    ④ CountDownLatch 不可以复用,

      CyclicBarrier 可以复用。

    3. Semaphore(信号量)

    Semaphore 是一个计数信号量,必须由获取它的线程释放。

    作用:多个共享资源互斥的使用!并发限流,限制可以访问某些资源的线程数量。

    Semaphore 只有3个操作:① 初始化 ② 增加 ③ 减少

    常用方法

    public void  acquire();// 获取一个许可
    public void  acquire(intpermits);// 获取permits个许可
    public void  release();// 释放一个许可
    public void  release(intpermits);// 释放permits个许可

    - acquire:用来获取一个许可,若无许可能够获得,则会一直等待,直到获得许可。

    - release:用来释放许可。注意,在释放许可之前,必须先获获得许可。

    这4个方法都会被阻塞,如果想立即得到执行结果,可以使用下面几个方法:

    // 尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
    public  boolean  tryAcquire();
    
    // 尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
    public  boolean  tryAcquire(longtimeout, TimeUnit unit);
    
    // 尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
    public  boolean  tryAcquire(intpermits);
    
    // 尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
    public  boolean  tryAcquire(intpermits, longtimeout, TimeUnit unit);

    代码演示:抢车位

    现有6辆车,但是只有3个停车位

    public class SemaphoreDemo {
        public static void main(String[] args) {
    
            // 信号量,只允许 3 个线程同时访问
            Semaphore semaphore = new Semaphore(3);
    
            for (int i = 0; i < 6; i++) {
                new Thread(() -> {
                    try {
                        semaphore.acquire(); // 获取许可,将信号量 +1,如果信号量已经满了,等待被释放为止!
                        System.out.println("Car" + Thread.currentThread().getName() + " >>> 抢到车位");
                        TimeUnit.SECONDS.sleep(new Random().nextInt(10));
                        System.out.println("离开车位 >>> Car" + Thread.currentThread().getName());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        semaphore.release(); // 释放,将信号量 -1,然后唤醒等待的线程!
                    }
                }, String.valueOf(i)).start();
            }
        }
    }

    ◇  总结

    CountDownLatch 是一个线程等待其他线程, CyclicBarrier 是多个线程互相等待。

    CountDownLatch 的计数是减 1 直到 0,CyclicBarrier 是加 1,直到指定值。

    CountDownLatch 是一次性的, CyclicBarrier 可以循环利用。

    CyclicBarrier 可以在最后一个线程达到屏障之前,选择先执行一个操作。

    Semaphore ,需要拿到许可才能执行,并可以选择公平和非公平模式。

  • 相关阅读:
    STL中队列queue的常见用法
    牛客网剑指offer第17题——树的子结构
    《剑指offer》网络大神所有习题题解
    牛客网剑指offer第4题——重建二叉树
    牛客网剑指offer第56题——删除链表中重复的节点
    图像处理中的求导问题
    hash_set和hash_map
    hashtable初步——一文初探哈希表
    数据结构-链表的那些事(下)(二)
    数据结构-链表的那些事(上)(一)
  • 原文地址:https://www.cnblogs.com/Dm920/p/13336381.html
Copyright © 2020-2023  润新知