可循环利用的过程,可以获得或者释放,当许可证的数量固定,获得许可证是类似”减法”的操作,减少许可证,释放类似于”加法”的操作,增加许可证。

使用场景

在实际生活中有很多的例子,比如停车场的车位数量有限,食堂的座位有限,这就需要控制入场的车或者人的数量,来维持秩序。
在计算机的世界里,Java并发靠线程来实现,线程之间的调度时间片切换靠CPU来管理,无节制的增加线程会加速消耗系统的资源,CPU飙升。

使用案例

使用Semaphore简单模拟停车场固定车位数量停车场景。

  1. package juc.semaphore;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.concurrent.Semaphore;
  5. import java.util.concurrent.TimeUnit;
  6. /**
  7. * Semaphore停车场景模拟
  8. * <p>
  9. * 简介:
  10. * 通过Semaphore来模拟停车场车辆停车,为防止循环一直无限循环下去,设置cycles循环次数,当达到次数后退出模拟。
  11. * <p>
  12. * 参数介绍:
  13. * cycles 循环总次数,curCycle 当前循环,permits 许可证即车位数量
  14. * <p>
  15. * 方法介绍:
  16. * checkCar()开启线程检验当前已获得停车位的汽车数量,并输出到控制台。
  17. * CheckExit()开启线程检验当前循环周期次数(即许可证为0的情况次数)并输出到控制台。
  18. * <p>
  19. * 补充:
  20. * 创建Thread模拟新到来的汽车,Car类模拟汽车的停车park()(获得许可证,parkCarList数量加一),离开leave()(释放许可证,parkCarList数量减一)
  21. * 通过time()方法来模拟过程启动,停车等延迟,防止启动就出现OOM的情况
  22. *
  23. * @author starsray
  24. * @date 2021/12/16
  25. */
  26. public class ParkingSpace {
  27. /**
  28. * 循环次数
  29. */
  30. private final int cycles = 5;
  31. /**
  32. * 当前循环
  33. */
  34. private int curCycle;
  35. /**
  36. * 许可证
  37. */
  38. private final int permits = 5;
  39. /**
  40. * 获得停车位的汽车列表
  41. */
  42. private static final List<Car> parkCarList = new ArrayList<>();
  43. /**
  44. * 信号量
  45. */
  46. private final Semaphore semaphore = new Semaphore(permits);
  47. public static void main(String[] args) {
  48. ParkingSpace parkingSpace = new ParkingSpace();
  49. parkingSpace.test();
  50. }
  51. private void test() {
  52. checkCar();
  53. CheckExit();
  54. while (true) {
  55. time(1, 1);
  56. Thread thread = new Thread(new Car());
  57. thread.start();
  58. }
  59. }
  60. private void checkCar() {
  61. Thread thread = new Thread(() -> {
  62. while (true) {
  63. try {
  64. TimeUnit.SECONDS.sleep(2);
  65. if (parkCarList.size() > 0) {
  66. System.out.println("current car list : " + parkCarList.size());
  67. }
  68. } catch (InterruptedException e) {
  69. e.printStackTrace();
  70. }
  71. }
  72. });
  73. thread.setDaemon(true);
  74. thread.start();
  75. }
  76. private void CheckExit() {
  77. Thread thread = new Thread(() -> {
  78. System.out.println("running...");
  79. while (true) {
  80. try {
  81. TimeUnit.SECONDS.sleep(5);
  82. int permits = semaphore.availablePermits();
  83. if (permits == 0) {
  84. curCycle++;
  85. }
  86. System.out.printf("when destroy left %s times\n", cycles - curCycle);
  87. if (curCycle == cycles) {
  88. System.out.println("Game Over!");
  89. System.exit(0);
  90. }
  91. } catch (InterruptedException e) {
  92. e.printStackTrace();
  93. }
  94. }
  95. });
  96. thread.setDaemon(true);
  97. thread.start();
  98. }
  99. class Car implements Runnable {
  100. @Override
  101. public void run() {
  102. work();
  103. }
  104. private void work() {
  105. park();
  106. leave();
  107. }
  108. private void leave() {
  109. semaphore.release(1);
  110. parkCarList.remove(this);
  111. }
  112. private void park() {
  113. try {
  114. time(1, 1);
  115. semaphore.acquire(1);
  116. parkCarList.add(this);
  117. time(10, 10);
  118. } catch (InterruptedException e) {
  119. semaphore.release(1);
  120. e.printStackTrace();
  121. }
  122. }
  123. }
  124. private void time(int init, int timeout) {
  125. int time = (int) (init + Math.random() * (timeout));
  126. try {
  127. TimeUnit.SECONDS.sleep(time);
  128. } catch (InterruptedException e) {
  129. e.printStackTrace();
  130. }
  131. }
  132. }

源码简析

RateLimiter

https://zhuanlan.zhihu.com/p/60979444