• 同步工具:CountDownLatch、CyclicBarrier和Semaphore


    1. CountDownLatch

    1.1 功能及使用场景

    一个同步工具,使得一个或多个线程等待一组线程执行完成后再执行。

    使用场景:等待一些前置任务执行完成后,再执行特定的功能。比如,系统启动时,各种配置生效后,才能运行提供服务。

    1.2 代码实例

    public class CountDownLatchTest {
    
        public static void main(String[] args) {
            final CountDownLatch latch = new CountDownLatch(5);
    
            for (int i = 0; i < 5; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + " end, " + System.currentTimeMillis());
                        latch.countDown();
                    }
                }).start();
            }
    
            try {
                System.out.println("main latch.await() " + System.currentTimeMillis());
                latch.await();
                System.out.println("main latch.await() end " + System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    
    }
    

    特别注意:CountDownLatch初始化时,数量一定要等于等待的线程的数量。

    2. CyclicBarrier

    2.1 功能及使用场景

    CyclicBarrier(可循环同步屏障),控制一组线程相互等待,直到所有线程都到达屏障点。所有线程都到达屏障点后,同时开始执行剩余的任务。

    可循环,意味着当所有线程都到达屏障后,屏障可以被再次重复利用。

    使用场景

    多个任务同时到达某个临界情况时,才能同时执行剩余任务,否则相互等待。

    2.2 示例代码

    public class CyclicBarrierTest {
    
        public static void main(String[] args) {
            int num = 3;
            final CyclicBarrier barrier = new CyclicBarrier(num);
            for (int i = 0; i < num; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                            System.out.println(Thread.currentThread().getName() + "执行结束,开始等待其它线程, " + System.currentTimeMillis());
                            barrier.await();
                            System.out.println(Thread.currentThread().getName() + "所有线程等待都执行完成,开始执行剩下任务, " + System.currentTimeMillis());
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } catch (BrokenBarrierException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
    
            // 屏障可以在上一次使用完成后被再次使用
            for (int i = 0; i < num; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                            System.out.println(Thread.currentThread().getName() + "执行结束,开始等待其它线程, " + System.currentTimeMillis());
                            barrier.await();
                            System.out.println(Thread.currentThread().getName() + "所有线程等待都执行完成,开始执行剩下任务, " + System.currentTimeMillis());
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } catch (BrokenBarrierException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
            System.out.println("main end at " + System.currentTimeMillis());
        }
    
    }
    

    3. Semaphore

    3.1 功能及使用场景

    Semaphore(信号量),一个计数信号量。概念上,一个信号量维持了一个许可集合。

    • acquire()方法:获取一个许可,如果没有获取到许可,则一直阻塞到获得一个许可;
    • release()方法:归还一个许可。

    实际上,并没有真正的许可对象,只是计数。

    使用场景

    限制资源同时被访问的线程数量

    3.2 使用代码

    public class SemaphoreTest {
    
        public static void main(String[] args) {
            final Semaphore semaphore = new Semaphore(3);
            for (int i = 0; i < 10; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            System.out.println(Thread.currentThread().getName() + "尝试获取许可, " + System.currentTimeMillis());
                            semaphore.acquire();
                            System.out.println(Thread.currentThread().getName() + "获取许可, " + System.currentTimeMillis());
                            TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                            System.out.println(Thread.currentThread().getName() + "执行结束, " + System.currentTimeMillis());
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } finally {
                            semaphore.release();
                        }
                    }
                }).start();
            }
            System.out.println("main end! " + System.currentTimeMillis());
        }
    
    }
    

    特别注意:获得许可并执行完逻辑后,一定要释放,否则许可不会被归还。(有借有还,再借不难)

    4. 总结

    • CountDownLatch:一个任务等待前置任务执行完后才能执行
    • CyclicBarrier: 一组任务相互等待,直到所有线程均到达某个临界状态
    • Semaphore: 一组许可,控制资源被同时访问的数量(获取许可,也要归还许可)
  • 相关阅读:
    StackStorm简介及其部署
    Nginx系列(十二)——性能调整
    Nginx系列(十一)——通过日志进行故障排查
    Nginx系列(十)——可用性监控进阶
    Nginx系列(九)——容器/微服务
    Nginx系列(八)——数字媒体流
    Nginx系列(七)——HTTP/2
    Nginx系列(六)——安全控制
    Nginx系列(五)——认证
    Nginx系列(四)——配置文件自动化管理
  • 原文地址:https://www.cnblogs.com/acode/p/8819425.html
Copyright © 2020-2023  润新知