Semaphore

Semaphore,俗称信号量,就类似于互斥锁,通过同时只能有一个线程获取信号量实现。大小为n(n>0)的信号量可以实现限流的功能,它可以实现只能有n个线程同时获取信号量。

PV操作是操作系统一种实现进程互斥与同步的有效方法

PV操作与信号量(S)的处理相关,P表示通过的意思,V表示释放的意思。用PV操作来管理共享资源时,首先要确保PV操作自身执行的正确性。

  • P操作的主要动作是:
    • S减1;
    • 若S减1后仍大于或等于0,则进程继续执行
    • 若S减1后小于0,则该进程被阻塞后放入等待该信号量的等待队列中,然后转进程调度
  • V操作的主要动作是:
    • S加1;
    • 若相加后结果大于0,则进程继续执行;
    • 若相加后结果小于或等于0,则从该信号的等待队列中释放一个等待进程,然后再返回原进程继续执行或转进程调度。

Semaphore流程

Semaphore window = new Semaphore(3),初始化的是非公平锁,信号量3是指是赋值给state。

  1. public static void main(String[] args) throws InterruptedException {
  2. //资源,3个窗口
  3. Semaphore window = new Semaphore(3);
  4. for (int i = 0; i < 5; i++) {
  5. new Thread(new Runnable() {
  6. @Override
  7. public void run() {
  8. try {
  9. //占用窗口,加锁
  10. window.acquire();
  11. System.out.println(Thread.currentThread().getName() + "开始买票");
  12. //模拟买票流程
  13. Thread.sleep(5000);
  14. System.out.println(Thread.currentThread().getName() + "买票成功");
  15. } catch (Exception e) {
  16. e.printStackTrace();
  17. } finally {
  18. window.release();
  19. }
  20. }
  21. }).start();
  22. }
  23. }

Thread0

  • 调用acquire是指调用acquireSharedInterruptibly,执行tryAcquireShared(arg),arg=1

截屏2022-03-23 23.35.05.png

  • tryAcquireShared,最终调用nonfairTryAcquireShared方法

03-23AQS之Semaphorer - 图2

  • 此时thread0获得锁成功,把status修改为2

Thread1

  • thread1执行thread0一样的操作,cas成功,把state修改为1

Thread2

  • thread2执行thread1一样的操作,cas成功,把state修改为0

Thread3

  • thread3执行thread2一样的流程,因为此时的remaining已经小于0了
  • 然后执行doAcquireSharedInterruptibly方法

03-23AQS之Semaphorer - 图3

  • 调用shouldParkAfterFailedAcquire,返回false

03-23AQS之Semaphorer - 图4

  • 然后开始第二轮循环,继续执行shouldParkAfterFailedAcquire,此时是返回true,然后执行parkAndCheckInterrupt

截屏2022-03-24 00.04.30.png

  • 把thread3挂起

Thread4

  • thread4和thread一样的流程,同样被挂起。
  • 此时的等待队列是:

03-23AQS之Semaphorer - 图6

解锁release

  • release,最终调用releaseShared方法,arg=1。releaseShared调用tryReleaseShared

截屏2022-03-24 00.13.52.png
03-23AQS之Semaphorer - 图8

  • 就是通过cas把state更新,把state加上releases,就相当于把资源放回去,此时的state=1。
  • 然后执行doReleaseShared

03-23AQS之Semaphorer - 图9

  • 此时的头节点的waitState是-1,然后进行cas,先把头节点的waitState修改为0,然后执行unparkSuccessor

03-23AQS之Semaphorer - 图10

  • 此时的thread3被唤醒,再去争抢锁,此时cas的tryAcquireShared会成功

03-23AQS之Semaphorer - 图11

  • 然后执行setHeadAndPropagate

03-23AQS之Semaphorer - 图12

  • doReleaseShared,做的东西就是把head节点的next节点(thread4)去唤醒。例如唤醒了thred4后,还有next节点的话,也会继续去唤醒下一个。

截屏2022-03-24 00.35.07.png