允许多个线程同时访问:信号量(Semaphore)

Posted 敲代码的程序狗

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了允许多个线程同时访问:信号量(Semaphore)相关的知识,希望对你有一定的参考价值。

信号量并不是Java中独有的功能,而是一种为并发编程提供的一种模型。本质上来说,信号量就是具有原子性的一个计数器,当使用了资源时,计数器就要减一,表示可用的资源就少了一个,当用完这个资源将其归还,计数器就加一,表示可用的资源又多个一个。

信号量模型

  1. init:设置计数器的初始值。
  2. down:计数器的值减1;如果此时计数器的值小于0,则当前线程被阻塞,否则当前线程可以继续执行。
  3. up:计数器的值加1;如果此时计数器的值小于或者等于0,则唤醒等待队列中的一个线程执行,并将其从等待队列中移除。

down和up操作也被称为PV操作(PV原语),对应java并发包下对应acquire()和release()。

Semaphore

Semaphore是JUC提供的信号量的封装,它是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。Semaphore 跟锁(synchronized、Lock)有点相似,不同的地方是,锁同一时刻只允许一个线程访问某一资源,而 Semaphore 则可以控制同一时刻多个线程访问某一资源。

它是一种基于计数的信号量。它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做自己的申请后归还,超过阈值后,线程申请许可信号将会被阻塞。Semaphore可以用来构建一些对象池,资源池之类的,比如数据库连接池,我们也可以创建计数为1的Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量,表示两种互斥状态。

Semaphore有两个构造函数:

public Semaphore(int permits)
public Semaphore(int permits, boolean fair)
复制代码

permits定义了许可资源的个数,而fair则表示是否支持FIFO的顺序。在构造信号量对象时,必须要指定信号量的准入数,即同时能申请多少个许可。当每个线程每次只申请一个许可时,这就相当于同时有多个线程可以访问某一个资源。主要的方法如下:

public void acquire()
public void acquireUniterruptibly()
public boolean tryAcquire()
public boolean tryAcquire(long timeout, TimeUnit unit)
public void release()
复制代码

acquire()方法尝试获取一个准入的许可。若无法获得,则线程会等待,直到有线程释放一个许可或者当前线程被中断。acquireUniterruptibly()与acquire()方法相似,但是其不响应中断。tryAcquire()和tryAcquire(long timeout, TimeUnit unit)会尝试获得一个许可,如果成功返回true,失败则返回false。release()用于在线程访问资源结束后,释放一个许可,使其他等待许可的线程可以进行资源访问。

使用场景

Semaphore主要有以下几种应用场景:

  1. 加锁(类似于ReentrantLock)
  2. 异步任务同步返回
  3. 控制线程并发数(限流)

其中限流是用的最多的,在我自己实现的simple-rpc中就有这样的应用,代码如下:

private Object invokeMethod(Provider provider, Request request, Semaphore semaphore) {
    Object serviceObject = provider.getServiceObject();
    Method serviceMethod = provider.getServiceMethod();
    Object result = null;
    boolean acquire = false;
    try {
        acquire = semaphore.tryAcquire(request.getInvokeTimeout(), TimeUnit.MILLISECONDS);
        if (acquire) {
            result = serviceMethod.invoke(serviceObject, request.getArgs());
        }
    } catch (Exception e) {
        result = e;
        log.error("NettyServerBizHandler invokeMethod error, provider={}, request={}", provider, request, e);
    } finally {
        if (acquire) {
            semaphore.release();
        }
    }
    return result;
}
复制代码

这里的主要作用就是在执行反射调用时,对每个接口进行限流操作。

以上是关于允许多个线程同时访问:信号量(Semaphore)的主要内容,如果未能解决你的问题,请参考以下文章

Java中Semaphore(信号量)的使用

同步与锁

Java多线程——Semaphore信号灯

并发编程中:Semaphore信号量与lock的区别

12第七周-网络编程 - 线程中的信号量(Semaphore)

Java并发工具类控制并发线程数的Semaphore