使用Semaphore限制资源并发访问的线程数

2023-02-23 15:43:53 浏览数 (1)

从JDK 1.5之后,在java.util.concurrent包下引入了好多的处理多线程的工具类,本文将介绍用来控制资源同时访问个数的Semaphore工具类, 然后采用Semaphore给出一个泊车的实例,最后给出SemaphoreCountDownLatch的几点比较。

一、Semaphore工具类介绍

1.1 Semaphore类描述

代码语言:javascript复制
/**
 * A counting semaphore.  Conceptually, a semaphore maintains a set of
 * permits.  Each {@link #acquire} blocks if necessary until a permit is
 * available, and then takes it.  Each {@link #release} adds a permit,
 * potentially releasing a blocking acquirer.
 * However, no actual permit objects are used; the <tt>Semaphore</tt> just
 * keeps a count of the number available and acts accordingly.
 *
 * <p>Semaphores are often used to restrict the number of threads than can
 * access some (physical or logical) resource.
 */

从Semaphore的注释中可以看出如下几点:

从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。 Semaphore并不使用实际的许可对象,Semaphore 只对可用许可进行计数,并采取相应的行动。 Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。

1.2 Semaphore工具类相关类图

Semaphore中定义了一个内部类Sync,该类继承AbstractQueuedSynchronizer。 从代码中可以看出,Semaphore的方法基本上都调用了Sync的方法来实现。Smaphore还提供了公平非公平的两种方式.

二、Semaphore工具类的使用案例

2.1 案例描述

本文给出一个使用Semaphore模式30辆车泊车的场景

车位有10个,当车位满时,只能先出来一辆车,然后才能进入一辆车。

2.2 代码与测试

代码语言:javascript复制
import java.util.concurrent.Semaphore;

/**
 * 
 * @author wangmengjun
 *
 */
public class Car implements Runnable {

  private final Semaphore parkingSlot;

  private int carNo;

  /**
   * @param parkingSlot
   * @param carName
   */
  public Car(Semaphore parkingSlot, int carNo) {
    this.parkingSlot = parkingSlot;
    this.carNo = carNo;
  }

  public void run() {

    try {
      parkingSlot.acquire();
      parking();
      sleep(300);
      leaving();
      parkingSlot.release();
      

    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

  }

  private void parking() {
    System.out.println(String.format("%d号车泊车", carNo));
  }

  private void leaving() {
    System.out.println(String.format("%d号车离开车位", carNo));
  }

  private static void sleep(long millis) {
    try {
      Thread.sleep(millis);
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();

    }
  }

}
代码语言:javascript复制
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * 
 * @author wangmengjun
 *
 */
public class ParkingCars {

  public static void main(String[] args) {

    Semaphore parkingSlot = new Semaphore(10, true);
    ExecutorService service = Executors.newCachedThreadPool();
    for (int carNo = 1; carNo <= 30; carNo  ) {
      service.execute(new Car(parkingSlot, carNo));
    }
  }
}

某次运行结果

代码语言:javascript复制
1号车泊车
7号车泊车
9号车泊车
5号车泊车
10号车泊车
6号车泊车
4号车泊车
3号车泊车
8号车泊车
2号车泊车
8号车离开车位
11号车泊车
3号车离开车位
13号车泊车
4号车离开车位
6号车离开车位
12号车泊车
14号车泊车
10号车离开车位
15号车泊车
5号车离开车位
9号车离开车位
16号车泊车
17号车泊车
7号车离开车位
18号车泊车
1号车离开车位
19号车泊车
2号车离开车位
20号车泊车
12号车离开车位
13号车离开车位
11号车离开车位
14号车离开车位
22号车泊车
21号车泊车
23号车泊车
24号车泊车
15号车离开车位
16号车离开车位
17号车离开车位
27号车泊车
25号车泊车
19号车离开车位
26号车泊车
20号车离开车位
28号车泊车
29号车泊车
18号车离开车位
30号车泊车
24号车离开车位
21号车离开车位
23号车离开车位
22号车离开车位
30号车离开车位
29号车离开车位
26号车离开车位
28号车离开车位
25号车离开车位
27号车离开车位

三、Semaphore 与CountDownLatch的比较

3.1 相同点

两者都是用于线程同步的工具类,都通过定义了一个继承AbstractQueuedSynchronizer的内部类Sync来实现具体的功能。

3.2 不同点

  • Semaphore提供了公平和非公平两种策略, 而CountDownLatch则不具备
  • CountDownLatch:一个或者是一部分线程,等待另外一部线程都完成操作。Semaphorr: 维护一个许可集.通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。
  • CountDownLatch中计数是不能被重置的。CountDownLatch适用于一次同步。当使用CountDownLatch时,任何线程允许多次调用countDown()。那些调用了await()方法的线程将被阻塞,直到那些没有被阻塞线程调用countDown()使计数到达0为止 。
  • Semaphore允许线程获取许可, 未获得许可的线程需要等待.这样防止了在同一时间有太多的线程执行。Semaphore的值被获取到后是可以释放的,并不像CountDownLatch那样一直减到0。

使用CountDownLatch时,它关注的一个线程或者多个线程需要在其它在一组线程完成操作之后,在去做一些事情。比如:服务的启动等。使用Semaphore时,它关注的是某一个资源最多同时能被几个线程访问。

由于知识的原因,上述例子以及CountDownLatch和Semaphore的比较上会存在不足,如果有问题请大家指正,也希望大家能够提供两者其它方面的不同之处,一起学习分享。

0 人点赞