• java 多线程 28 : 多线程组件之 Semaphore 信号量


    Semaphore是非常有用的一个组件,它相当于是一个并发控制器,是用于管理信号量的。构造的时候传入可供管理的信号量的数值,这个数值就是控制并发数量的,就是同时能几个线程访问。我们需要控制并发的代码,执行前先通过acquire方法获取信号,执行后通过release归还信号 。每次acquire返回成功后,Semaphore可用的信号量就会减少一个,如果没有可用的信号,acquire调用就会阻塞,等待有release调用释放信号后,acquire才会得到信号并返回。


    ps:注意这里信号量acquire方法和release方法是可以有参数的,表示获取/返还的信号量个数,如果不指定就是默认单个释放


    Semaphore实现的功能就类似厕所有5个坑,假如有10个人要上厕所,那么同时只能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中 的任何一个人让开后,其中等待的另外5个人中又有一个人可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。Semaphore在实现这种排队机制的时候很优秀,代码简洁

    Semaphore维护了当前访问的个数,提供同步机制,控制同时访问的个数。在数据结构中链表可以保存“无限”的节点,用Semaphore可以实现有限大小的链表。另外重入锁 ReentrantLock 也可以实现该功能,但实现上要复杂些。


    ReentrantLock 实现的生产/消费者一对一情况下 ,对比Semaphore 代码更复杂,但是在多个生产/消费的况,ReentrantLock虽然代码比较复杂,但是更高效 

    Semaphore分为单值和多值两种:

    1、单值的Semaphore管理的信号量只有1个,该信号量只能被1个,只能被一个线程所获得,意味着并发的代码只能被一个线程运行,这就相当于是一个互斥锁了

    2、多值的Semaphore管理的信号量多余1个,主要用于控制并发数

    看一下代码例子:

    public static void main(String[] args)
    {
        final Semaphore semaphore = new Semaphore(5);
            
        Runnable runnable = new Runnable()
        {
            public void run()
            {
                try
                {
                    semaphore.acquire();                    
             System.out.println(Thread.currentThread().getName()
    + "获得了信号量,时间为" + System.currentTimeMillis()); Thread.sleep(2000);          System.out.println(Thread.currentThread().getName() + "释放了信号量,时间为" + System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } } }; Thread[] threads = new Thread[10]; for (int i = 0; i < threads.length; i++) threads[i] = new Thread(runnable); for (int i = 0; i < threads.length; i++) threads[i].start(); }

    看一下运行结果:

     1 Thread-1获得了信号量,时间为1444557040464
     2 Thread-2获得了信号量,时间为1444557040465
     3 Thread-0获得了信号量,时间为1444557040464
     4 Thread-3获得了信号量,时间为1444557040465
     5 Thread-4获得了信号量,时间为1444557040465
     6 Thread-2释放了信号量,时间为1444557042466
     7 Thread-4释放了信号量,时间为1444557042466
     8 Thread-0释放了信号量,时间为1444557042466
     9 Thread-1释放了信号量,时间为1444557042466
    10 Thread-3释放了信号量,时间为1444557042466
    11 Thread-9获得了信号量,时间为1444557042467
    12 Thread-7获得了信号量,时间为1444557042466
    13 Thread-6获得了信号量,时间为1444557042466
    14 Thread-5获得了信号量,时间为1444557042466
    15 Thread-8获得了信号量,时间为1444557042467
    16 Thread-9释放了信号量,时间为1444557044467
    17 Thread-6释放了信号量,时间为1444557044467
    18 Thread-7释放了信号量,时间为1444557044467
    19 Thread-5释放了信号量,时间为1444557044468
    20 Thread-8释放了信号量,时间为1444557044468

    前10行为一部分,运行的线程是1 2 0 3 4,看到时间差也都是代码约定的2秒;后10行为一部分,运行的线程是9 7 6 5 8,时间差也都是约定的2秒,这就体现出了Semaphore的作用了,这里由于是在中间使用sleep ,所以看起来是有序的,必须释放5个才能获取,其实不然,是一个释放,信号量发现还有空余的就会立刻分给下一个等待的线程

    这种通过Semaphore控制并发并发数的方式和通过控制线程数来控制并发数的方式相比,粒度更小,因为Semaphore可以通过acquire方法和release方法来控制代码块的并发数。

    最后注意两点:

    1、Semaphore可以指定公平锁还是非公平锁

    2、acquire方法和release方法是可以有参数的,表示获取/返还的信号量个数



  • 相关阅读:
    【LeetCode OJ】Longest Substring Without Repeating Characters
    JavaScript学习笔记一
    Struts2(五)数据校验
    Struts2(四)属性驱动和模型驱动
    php知识大集合(自用)
    正则表达式匹配非需要匹配的字符串(标题自己都绕晕了)
    php中ereg() ,preg_match() 与preg_match_all的区别?代码详细比较
    linux 查询当前文件夹下的目录数量
    linux 下如何查看mysql跑了哪些服务
    mysql同主机下 复制一个数据库所有文件到另一个数据库
  • 原文地址:https://www.cnblogs.com/signheart/p/3248461b2037f5df3b61f76b41410587.html
Copyright © 2020-2023  润新知