zoukankan      html  css  js  c++  java
  • Semaphore信号量

    Semaphore,等待指定数量的线程完成任务即可

    public class A {
    
        private static SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        // 同步关键类,构造方法传入的数字是多少,则同一个时刻,只运行多少个进程同时运行制定代码
        private Semaphore semaphore = new Semaphore(2);
    
        public static void main(String[] args) throws Exception{
            A a = new A();
            for (int i = 0; i < 10; i++) {
                new Thread(()->{
                    a.doSomething();
                }).start();
            }
        }
    
    
        public void doSomething() {
            try {
                /**
                 * 在 semaphore.acquire() 和 semaphore.release()之间的代码,同一时刻只允许制定个数的线程进入,
                 * 因为semaphore的构造方法是1,则同一时刻只允许一个线程进入,其他线程只能等待。
                 * */
                semaphore.acquire();
                System.out.println(Thread.currentThread().getName() + ": start-" + getFormatTimeStr());
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + ": end-" + getFormatTimeStr());
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static String getFormatTimeStr() {
            return sf.format(new Date());
        }
    }
    View Code

    初始化

      初始化的时候,就是将 permits 设置给 state,标记占用锁标识。state的值就代表当前所剩余的令牌数量。

     

    acquire()

    获取令牌

      在 tryAcquireShared() 里面,第一个线程会先获取到当前的令牌数量,我们初始化的时候是2,remaining = 2-1>0 ,所以走cas操作赋值,等到第三个线程来的时候,则 remaining = 0-1>0,此时第三个线程就没获取到令牌了,走下面的 doAcquireSharedInterruptibly() 方法。

    挂起线程

    获取到令牌的线程就继续执行它的业务逻辑了,没有获取到令牌的线程就会来到这里。
    1. addWaiter(Node.SHARED); 首先创建节点加入阻塞队列
    2. node.predecessor(); 获取到上一个节点p,如果上一个节点是头节点,说明自己排在第一个。
    3. 如果自己是第一个线程就 tryAcquireShared() 尝试cas获取令牌。(因为可能走到这里时,别人释放了令牌啊)
    4. 然后 r>=0 就获取到了令牌,那就把当前线程从对等队列里面摘取出来
    5. 如果不是第一个排队的节点,那就在 parkAndCheckInterrupt() 里面通过 park() 操作把自己挂起来,等待其他线程来把它唤醒

    release()

    释放令牌

      在 tryReleaseShared() 里面将 state 加一,然后cas操作执行成功就去唤醒等待队列中的节点。

    唤醒其他节点

    1. 判断头节点 h 的状态 Node.SIGNAL 是否需要唤醒后继节点
    2. 进行 cas 操作修改状态为初始0
    3. unparkSuccessor(h) 唤醒 h.next 节点

  • 相关阅读:
    JavaScript开发中几个常用知识点总结
    编写高质量代码改善C#程序的157个建议[勿选List<T>做基类、迭代器是只读的、慎用集合可写属性]
    编写高质量代码改善C#程序的157个建议[泛型集合、选择集合、集合的安全]
    C#基础知识系列十(集合)
    Json.Net6.0入门学习试水篇
    编写高质量代码改善C#程序的157个建议[动态数组、循环遍历、对象集合初始化]
    C#基础知识系列九(对IEnumerable和IEnumerator接口的糊涂认识)
    Asp.Net MVC3.0项目部署到Win7 64过程总结
    .sql文件l通过PLSQL导入到Oracle数据库
    PowerDesigner工具将表字段转成java实体
  • 原文地址:https://www.cnblogs.com/wlwl/p/15054814.html
Copyright © 2011-2022 走看看