• Semaphore信号量原理


    Semaphore信号量原理

    • Semaphore是一个计数信号量,本质是"共享锁".
    • Semaphore维护一个信号量许可集.
    • 线程可以调用acquire()获得信号量的许可.
    • 线程可以调用release()释放其持有的信号量.
    • 使用acquire()请求获得信号量时,若信号量有可用的许可时,线程获得许可,并且当前可用的许可减1.若许可集已经分配完,则线程进入等待状态,直到其他线程释放许可才有机会再获得许可.
    • 使用release()释放信号量时,当前可用的信号量加1.
    • new Semaphore(int permits)设置初始的许可数.
    • acquire(int permits)获取指定数量的许可,若获取成功,则执行,否则线程阻塞等待条件满足.
    • acquire(0),表明不需要获得许可即可继续执行.若后续使用release(),则信号量的总许可数加1.
    • 若初始化许可设为0,则使用acquire(0)可继续执行,若使用acquire()或请求正整数个许可,则线程一直处于请求状态.

    Semaphore函数列表

    Semaphore(int permits) // 给定许可数量的非公平信号量
    Semaphore(int permits, boolean fair) // 给定许可数量,设置公平的信号量
    
    void acquire() // 从信号量中获得一个许可,若不足,则线程阻塞
    void acquire(int permits) // 获得指定数量的许可
    
    int availablePermits() // 获得当前可用的许可数
    
    int drainPermits() // 摧毁剩下的许可
    
    protected Collection<Thread> getQueuedThreads() // 返回等待获取的线程
    

    内部类

    Semaphore有一个基于AQS的内部类Sync.基于这个类,分别实现了公平模式(FairSync)和非公平模式(NonFairSync).

    若初始化为1,则最多只有一个可用的许可证,可作为互斥锁使用,被称为二进制信号量.
    其只有两个状态:
    * 允许可用.
    * 不允许可用
    不同于锁,二进制信号量可以由持有者以外的线程释放.(可用于死锁恢复)

    非公平模式
    不保证线程获取许可的顺序.
    一个调用acquire()的线程可能先于等待很久的线程获得许可.(将新线程放在等待队列头)

    公平模式
    保证获得许可的顺序为线程调用acquire()方法的顺序.中

    实现原理

    内部类Sync继承于AQS(维护同步队列,控制同步状态).
    实现两种锁:

    • 公平锁(FairSync)
    • 非公平锁(NoFairSync)

    默认构造的为非公平锁.

    核心方法

    acquire()

    1. 若state值代表的许可数足够使用,则请求的线程将获得同步状态(对共享资源的访问权,并更新state值)
    2. 若state代表的许可数为0,则请求的线程无法获得同步状态,线程被加入到同步队列中并阻塞,直到其他线程释放同步状态才能获得对共享资源的访问.

    细节:

    • 是可中断的.先检查线程的中断情况.若有中断,则抛出异常.若没有中断,则尝试获取同步状态.
    • 获取方式:先获取状态state,再对状态进行减法操作,并使用CAS保证对state状态的修改的安全性.
    • 进入队列的操作:当前线程自旋操作:判断前驱节点是否为head节点.若是,则获取同步状态.获取成功,则将当前节点设为head节点,并向后传播(同步状态剩余值大于0,通知后续节点继续获取同步状态)

    注:

    • 对于不可中断的,少了中断的判断以及异常的抛出.

    release()

    • 调用AQS中的方法尝试CAS释放同步状态.若释放成功,则唤醒同步队列中后继节点的线程.
    • 释放细节:获得当前状态,然后采用CAS方式更新state.
    • 唤醒后继节点的细节:
      • 获取head节点的状态,若为SIGNAL,则后继节点需要被唤醒.若head节点的后继结点为空或不可唤醒,则从队列尾端向前查找最靠前的可唤醒的节点进行唤醒.(LockSupport的unpark()方法)

    公平锁

    • 与非公平锁的不同在于:在尝试获取同步状态时,先判断同步队列中是否存在节点.若存在,则将线程加入到同步等待队列中,从而保证先到的线程一定先执行.

    小结

    • 通过AQS对状态值state进行控制,从而控制并发访问资源的线程数.
    • 线程在成功获取资源后,状态值state会减一.
    • 若超过限制的线程请求资源,则线程会加入等待队列中,直到其他线程执行释放同步状态,才有机会获得访问权.
    • 每个线程在释放同步状态后,状态值state会加1.

    参考:

  • 相关阅读:
    DHCP服务器与DHCP中继服务器实验
    DAY1-作业
    logging模块的基本使用
    01_docker镜像命令
    00_docker的基本组成
    21_django配置使用mysql数据库的两种方式
    08_使用python操作mysql
    07_mysql的基本操作
    06_python操作mongodb
    05_MongoDB基本操作
  • 原文地址:https://www.cnblogs.com/truestoriesavici01/p/13235985.html
Copyright © 2020-2023  润新知