美文网首页
7、那些基于AQS的同步器

7、那些基于AQS的同步器

作者: 神秘空指针 | 来源:发表于2018-09-29 19:20 被阅读0次

    之前说过AQS 是所有同步器的基础,那么我们就来说说那些基于AQS的同步工具吧

    1、Semaphore 信号量

    Semaphore(信号量)是一个控制并发数量两的同步器(吐槽:这名字和功能有毛线关系啊),他的构造接受一个int 参数,代表着并发数,当并发数,超过这个数就去队列中排队吧(这和读写锁中 读锁不是异曲同工吗,只不过读锁无法设置共享的线程数)。原理很好理解的吧,下面看看用法:

     public static void main(String[] args) {
    
            Semaphore semaphore = new Semaphore(5);
    
            for (int i = 0; i < 6; i++) {
                new Thread(() -> {
                    try {
                        String name = Thread.currentThread().getName();
                        System.out.println(name + " 我来上厕所了");
                        boolean b = semaphore.tryAcquire();
                        if (!b){
                            System.out.println(name + " 我没抢到坑位了");
                        }
                        System.out.println(name + " 我抢到一个坑,还剩" + semaphore.availablePermits() + "坑位");
                        Thread.sleep(3000);
                        semaphore.release();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }).start();
            }
        }
    

    这段程序 创建了一个信号量 控制并发数5个,启动六个线程取上厕所。相当于六个人去抢五个厕所啊!我们看看结果


    image.png

    2号线程开始没有抢到,被人用完了,才抢到的,要是没有用信号量控制,岂不是2号线程要在别人头上拉屎了吗?

    综上:Semaphore 是和用于资源有限的共享情况,比如数据库连接池等。

    2、CountDownLatch 计数器

    CountDownLatch 是一个计数器,当计数器不到阈值(0)程序会一直等待。CountDownLatch 和Semaphore 提供了一个带有int参数的构造器。提供了一个countDown() (每次调用计数器减一)。他适合在多个任务需要相互协作的场景,比如 王者荣耀 需要五个人才可以开始游戏。我们可以设置一个数量为5的计数器,每一个人进入游戏就countDown 一次,当计数器为0的时候,游戏就可以开始啦。
    下面看看用法:

     public static void main(String[] args) throws Exception {
    
            final CountDownLatch countDownLatch = new CountDownLatch(5);
    
            for (int i = 1; i < 6; i++){
                final int num = i;
                new Thread(() -> {
    
                    System.out.println("我是第" + num +" 个玩家,我进入游戏啦。");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    countDownLatch.countDown();
                }).start();
            }
            //等待计数器到达阈值
            countDownLatch.await();
            System.out.println("终于有五个人了啊,开始游戏吧!");
        }
    

    countDownLatch.await() 这个方法会一直等待计数器到达阈值,如果这个方法只有四个人进入游戏,那么这局游戏永远无法开始啦,不信你试试。

    3、CyclicBarrier 回环栅栏

    之前用游戏这个案例来说明CountDownLatch 的用法,但是我们发现一个很严重的问题,那就是游戏是要一直开始,每五个人就要开始一局的,那么计数器用一次岂不是用不了了? 别担心 我们还有CyclicBarrier 。
    CyclicBarrier 在我看来就是一个可以无限使用的计数器,它只要每达到阈值就会重新开始,
    下面看看用法吧

     public static void main(String[] args) throws Exception {
    
            String[] arr = {"小明", "小陈", "小牛", "大神", "菜鸡"};
          //CyclicBarrier 构造器 接受两个参数 1、阈值 2、到达阈值后的操作(为空则什么都不做)
            CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> {
                System.out.println("有五个玩家加入游戏,游戏开始!!!!!!!!");
            });
    
            for (int i = 0; i < 10; i++) {
                Thread thread = new Thread(() -> {
                    try {
                        Thread.sleep(new Random().nextInt(10000));
                        System.out.println(Thread.currentThread().getName() + "加入排位,总共有" + (cyclicBarrier.getNumberWaiting() + 1) + "玩家在等待");
                      //回环等待,等到阈值
                        cyclicBarrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }, arr[i / 5]);
                thread.start();
            }
        }
    

    看看结果:


    image.png

    从结果上看 每五个人进入游戏,CyclicBarrier 就只执行一次 事先内置的任务。
    使用上他和CountDownLatch 还是很有很大差别的。CountDownLatch是计算器,只有煮线程在等待计数器结果,其他线程处理完任务就完事了,而CyclicBarrier 是一个屏障,所有的线程遇到屏障都会停下来,等到集齐突破屏障的条件(例子中是五人进入游戏) 在一起处理,并且可以触发事先预留的处理事件(简直就像是集齐七颗龙珠召唤神龙一样 有木有!)。

    相关文章

      网友评论

          本文标题:7、那些基于AQS的同步器

          本文链接:https://www.haomeiwen.com/subject/yqxboftx.html