当前位置:   article > 正文

Semaphore使用及原理解读_java semaphore

java semaphore

使用

概述

Semaphore(信号量)是Java中一个并发控制工具,用于控制对共享资源的访问。它基于计数器的原理,可以限制同时访问某个资源的线程数量。

在Java中使用Semaphore,你需要按照以下步骤进行操作:

导包: 

import java.util.concurrent.Semaphore;

 创建Semaphore对象:

Semaphore semaphore = new Semaphore(n);

其中,n是允许同时访问共享资源的线程数量。

在需要访问共享资源的代码段前后,使用acquire()和release()方法来获取和释放信号量:

  1. try {
  2. semaphore.acquire(); // 获取信号量,如果没有可用的许可证,线程将被阻塞
  3. // 访问共享资源的代码
  4. } catch (InterruptedException e) {
  5. // 处理中断异常
  6. } finally {
  7. semaphore.release(); // 释放信号量,增加一个许可证
  8. }

acquire()方法尝试获取一个许可证,如果当前没有可用的许可证,则该线程将被阻塞,直到有可用的许可证为止。release()方法释放一个许可证,使其可供其他线程使用。

通过适当地使用acquire()release()方法,在超过信号量允许的线程数量时,可以限制并发访问共享资源的线程数量,实现线程间的同步和互斥。

需要注意的是,Semaphore还提供了一些其他方法,如availablePermits()用于获取当前可用的许可证数量,以及tryAcquire()方法在不阻塞线程的情况下尝试获取许可证等。

具体例子 
  1. public static void main(String[] args) {
  2. // 1. 创建 semaphore 对象
  3. Semaphore semaphore = new Semaphore(3);
  4. // 2. 10个线程同时运行
  5. for (int i = 0; i < 10; i++) {
  6. new Thread(() -> {
  7. // 3. 获取许可
  8. try {
  9. semaphore.acquire();
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. try {
  14. log.debug("running...");
  15. sleep(1);
  16. log.debug("end...");
  17. } finally {
  18. // 4. 释放许可
  19. semaphore.release();
  20. }
  21. }).start();
  22. }
  23. }

07:35:15.485 c.TestSemaphore [Thread-2] - running...

07:35:15.485 c.TestSemaphore [Thread-1] - running...

07:35:15.485 c.TestSemaphore [Thread-0] - running...

07:35:16.490 c.TestSemaphore [Thread-2] - end...

07:35:16.490 c.TestSemaphore [Thread-0] - end...

07:35:16.490 c.TestSemaphore [Thread-1] - end...

07:35:16.490 c.TestSemaphore [Thread-3] - running...

07:35:16.490 c.TestSemaphore [Thread-5] - running...

07:35:16.490 c.TestSemaphore [Thread-4] - running...

07:35:17.490 c.TestSemaphore [Thread-5] - end...

07:35:17.490 c.TestSemaphore [Thread-4] - end...

07:35:17.490 c.TestSemaphore [Thread-3] - end...

07:35:17.490 c.TestSemaphore [Thread-6] - running...

07:35:17.490 c.TestSemaphore [Thread-7] - running...

07:35:17.490 c.TestSemaphore [Thread-9] - running...

07:35:18.491 c.TestSemaphore [Thread-6] - end...

07:35:18.491 c.TestSemaphore [Thread-7] - end...

07:35:18.491 c.TestSemaphore [Thread-9] - end...

07:35:18.491 c.TestSemaphore [Thread-8] - running...

07:35:19.492 c.TestSemaphore [Thread-8] - end...  

源码原理解析

加锁解锁流程原理

Semaphore 有点像一个停车场,permits 就好像停车位数量,当线程获得了 permits 就像是获得了停车位,然后 停车场显示空余车位减一 刚开始,permits(state)为 3,这时 5 个线程来获取资源

假设其中 Thread-1,Thread-2,Thread-4 cas 竞争成功,而 Thread-0 和 Thread-3 竞争失败,进入 AQS 队列park 阻塞

接下来 Thread-0 竞争成功,permits 再次设置为 0,设置自己为 head 节点,断开原来的 head 节点,unpark 接 下来的 Thread-3 节点,但由于 permits 是 0,因此 Thread-3 在尝试不成功后再次进入 park 状态

+

源码 

 构造方法有俩个: 

  1. public Semaphore(int permits) {
  2. sync = new NonfairSync(permits);
  3. }

创建 Semaphore 具有给定数量的许可和不公平公平设置。
参数:
许可证 – 可用的许可证的初始数量。此值可能为负数,在这种情况下,必须先进行释放,然后才能授予任何收购。 

  1. public Semaphore(int permits, boolean fair) {
  2. sync = fair ? new FairSync(permits) : new NonfairSync(permits);
  3. }

创建具有给定数量的许可和给定公平性设置的 。Semaphore
参数:
许可证 – 可用的许可证的初始数量。此值可能为负数,在这种情况下,必须先进行释放,然后才能授予任何收购。
公平 – true 如果此信号量将保证在争用中授予先进先出的许可证,否则 false 

  1. static final class NonfairSync extends Sync {
  2. private static final long serialVersionUID = -2694183684443567898L;
  3. NonfairSync(int permits) {
  4. // permits 即 state
  5. super(permits);
  6. }
  7. // Semaphore 方法, 方便阅读, 放在此处
  8. public void acquire() throws InterruptedException {
  9. sync.acquireSharedInterruptibly(1);
  10. }
  11. // AQS 继承过来的方法, 方便阅读, 放在此处
  12. public final void acquireSharedInterruptibly(int arg)
  13. throws InterruptedException {
  14. if (Thread.interrupted())
  15. throw new InterruptedException();
  16. if (tryAcquireShared(arg) < 0)
  17. doAcquireSharedInterruptibly(arg);
  18. }
  19. // 尝试获得共享锁
  20. protected int tryAcquireShared(int acquires) {
  21. return nonfairTryAcquireShared(acquires);
  22. }
  23. // Sync 继承过来的方法, 方便阅读, 放在此处
  24. final int nonfairTryAcquireShared(int acquires) {
  25. for (;;) {
  26. int available = getState();
  27. int remaining = available - acquires;
  28. if (
  29. // 如果许可已经用完, 返回负数, 表示获取失败, 进入 doAcquireSharedInterruptibly
  30. remaining < 0 ||
  31. // 如果 cas 重试成功, 返回正数, 表示获取成功
  32. compareAndSetState(available, remaining)
  33. ) {
  34. return remaining;
  35. }
  36. }
  37. }
  38. // AQS 继承过来的方法, 方便阅读, 放在此处
  39. private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {
  40. final Node node = addWaiter(Node.SHARED);
  41. boolean failed = true;
  42. try {
  43. for (;;) {
  44. final Node p = node.predecessor();
  45. if (p == head) {
  46. // 再次尝试获取许可
  47. int r = tryAcquireShared(arg);
  48. if (r >= 0) {
  49. // 成功后本线程出队(AQS), 所在 Node设置为 head
  50. // 如果 head.waitStatus == Node.SIGNAL ==> 0 成功, 下一个节点 unpark
  51. // 如果 head.waitStatus == 0 ==> Node.PROPAGATE
  52. // r 表示可用资源数, 为 0 则不会继续传播
  53. setHeadAndPropagate(node, r);
  54. p.next = null; // help GC
  55. failed = false;
  56. return;
  57. }
  58. }
  59. // 不成功, 设置上一个节点 waitStatus = Node.SIGNAL, 下轮进入 park 阻塞
  60. if (shouldParkAfterFailedAcquire(p, node) &&
  61. parkAndCheckInterrupt())
  62. throw new InterruptedException();
  63. }
  64. } finally {
  65. if (failed)
  66. cancelAcquire(node);
  67. }
  68. }
  69. // Semaphore 方法, 方便阅读, 放在此处
  70. public void release() {
  71. sync.releaseShared(1);
  72. }
  73. // AQS 继承过来的方法, 方便阅读, 放在此处
  74. public final boolean releaseShared(int arg) {
  75. if (tryReleaseShared(arg)) {
  76. doReleaseShared();
  77. return true;
  78. }
  79. return false;
  80. }
  81. // Sync 继承过来的方法, 方便阅读, 放在此处
  82. protected final boolean tryReleaseShared(int releases) {
  83. for (;;) {
  84. int current = getState();
  85. int next = current + releases;
  86. if (next < current) // overflow
  87. throw new Error("Maximum permit count exceeded");
  88. if (compareAndSetState(current, next))
  89. return true;
  90. }
  91. }
  92. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/545648
推荐阅读
相关标签
  

闽ICP备14008679号