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 节点

  • 相关阅读:
    python基础学习(九)
    python基础学习(八)
    python基础学习(七)
    python基础学习(六)
    python基础学习(五)
    python基础学习(四)
    python基础学习(三)
    mysql-binlog server的实现
    percona-toolkit常用工具
    Linux下如何快速定位系统瓶颈在哪里
  • 原文地址:https://www.cnblogs.com/wlwl/p/15054814.html
Copyright © 2011-2022 走看看