允许多个线程同时访问:信号量(Semaphore)
Posted 敲代码的程序狗
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了允许多个线程同时访问:信号量(Semaphore)相关的知识,希望对你有一定的参考价值。
信号量并不是Java中独有的功能,而是一种为并发编程提供的一种模型。本质上来说,信号量就是具有原子性的一个计数器,当使用了资源时,计数器就要减一,表示可用的资源就少了一个,当用完这个资源将其归还,计数器就加一,表示可用的资源又多个一个。
信号量模型
- init:设置计数器的初始值。
- down:计数器的值减1;如果此时计数器的值小于0,则当前线程被阻塞,否则当前线程可以继续执行。
- 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主要有以下几种应用场景:
- 加锁(类似于ReentrantLock)
- 异步任务同步返回
- 控制线程并发数(限流)
其中限流是用的最多的,在我自己实现的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)的主要内容,如果未能解决你的问题,请参考以下文章