• Java随笔-Semaphore


    概述

    原理
    Semaphore(计数信号量)用来控制同时访问特定资源的线程数量,通过协调多线程,确保资源被合理使用。正常的锁在任何时刻都是只允许一个任务访问一项资源,而Semaphore允许多个任务同时访问一项资源。某些程度上和线程池有些相似,可用的数量固定,线程使用完后,剩下的进行排队,当有线程使用完毕后,放回线程池,然后再分配给排队的任务进行使用。
    作用
    Semaphore用来限制线程并发数量,用于限流,对访问的资源进行数量限制,比如:视频账号只允许最多两端同时在线,手机和电脑可以同时在线,若是想在平板上登录,手机和电脑中必须有一端退出登录才行;疫苗接种房间最多同时接种10人,接种者进入房间前发放许可证,只有手持许可证才能进入房间接种,10张许可证发放完后,剩下的人排队等着,接种完后收回许可证发放给后面排队的人进行接种。

    主要方法描述

    1. public Semaphore(int permits)
      permits:获取许可线程的数量,默认是不公平的设置。
    2. public Semaphore(int permits, boolean fair)
      permits:获取许可线程的数量,fair:true表示线程启动顺序和获得许可的顺序,先启动的先获取,当然只是理论保证,实际中无法确保100%,false表示线程启动顺序和获得许可顺序无关,有些线程快,有些线程慢,每次都会不一样。
    3. acquire()
      获取许可,也就是消耗一个许可。
    4. acquire(int permits)
      每次调用消耗permits个许可。
    5. acquireUninterruptibly()
      消耗一个许可,不允许等待线程中断。
    6. acquireUninterruptibly(int permits)
      一次获取permits个许可,且等待线程不被中断。
    7. tryAcquire()
      尝试获取许可,并不阻塞线程,获取失败返回false。
    8. tryAcquire(long timeout, TimeUnit unit)
      指定时间内获取许可,没有获取成功只要在指定时间内就轮询获取,超时获取失败返回false,不阻塞线程。
    9. release()
      释放一个许可,并唤醒一个正在阻塞的线程。
    10. release(int permits)
      一次释放permits许可。
    11. availablePermits()
      获取可用许可数量。
    12. drainPermits()
      获取并返回所有可用许可,并将可用许可置为0。
    13. reducePermits(int reduction)
      减少可用许可数量,不阻塞线程。
    14. getQueueLength()
      返回正在等待获取许可的线程数量。
    15. hasQueuedThreads()
      查询是否还有正在等待获取许可的线程。

    使用

            // 5张许可证
            final Semaphore semaphore = new Semaphore(5);
            // 使用线程池
            ExecutorService executorService = Executors.newCachedThreadPool();
    
            for (int i = 0; i < 10; i++) {
                int finalI = i;
                Runnable runnable = () -> {
                    try {
                        // 获取许可
                        semaphore.acquire();
                        System.out.println("第" + finalI + "位开始接种");
                        // 睡眠
                        Thread.sleep(new Random().nextInt(10000));
                        System.out.println("第" + finalI + "位接种完毕");
                        // 释放许可
                        semaphore.release();
    
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                };
                executorService.execute(runnable);
            }
            executorService.shutdown();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    结果:

    0位开始接种
    第1位开始接种
    第2位开始接种
    第3位开始接种
    第4位开始接种
    第3位接种完毕
    第8位开始接种
    第4位接种完毕
    第5位开始接种
    第1位接种完毕
    第9位开始接种
    第8位接种完毕
    第7位开始接种
    第5位接种完毕
    第6位开始接种
    第7位接种完毕
    第0位接种完毕
    第2位接种完毕
    第6位接种完毕
    第9位接种完毕
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    观察结果发现,0、1,2、3、4满5位后就没有开始接种的,只有第3位接种完毕后,空出一个位置,这个位置给了第8位,第4位接种完毕后空位给了第5位,第1位接种完毕后空出的位置给了第9位,总而言之,就是空出一位顶上一位,直到所有人接种完毕。

  • 相关阅读:
    【数据结构练习】树和二叉树的选择题精选集锦
    2512. 奖励最顶尖的 K 名学生
    基于LVM通过添加硬盘实现分区扩容的方法介绍
    从源码入手详解ReentrantLock,一个比synchronized更强大的可重入锁
    优化 Redis 集群缓存分配:解决节点间分配不均导致内存溢出问题
    从头开始使用 KNN 进行 KNN 和 MNIST 手写数字识别的初学者指南
    深度 | 车载语音群雄并起共争智能座舱新高地
    微服务主流框架概览
    Dubbo各种协议
    制造业企业库存管理的现状与解决措施
  • 原文地址:https://blog.csdn.net/qq_34202054/article/details/123663897