Semaphore是什么?
Semaphore简介semaphore英文翻译为信号标,它是能控制允许多少线程去访问资源,也可以反馈线程在指定时间是否获得可执行权的信号,从而对这些超时未获取权力的线程做另行安排。Semaphore使用场景从简介中可以得知,semaphore可以限制并发访问数量,即为限流,像是微服务组件hystrix、sentinel等限流中间件都可使用此原理,当然随之而来,那些被限制通行的线程是被丢弃?还是去
·
Semaphore简介
semaphore英文翻译为信号标,它是能控制允许多少线程去访问资源,也可以反馈线程在指定时间是否获得可执行权的信号,从而对这些超时未获取权力的线程做另行安排。
Semaphore使用场景
从简介中可以得知,semaphore可以限制并发访问数量,即为限流,像是微服务组件hystrix、sentinel等限流中间件都可使用此原理,当然随之而来,那些被限制通行的线程是被丢弃?还是去执行另外的流程,就是“降级服务”。
Semaphore使用demo
1、定义线程任务+降级服务
@Slf4j
public class DegradeTask extends Thread{
Semaphore semaphore;
public DegradeTask(Semaphore semaphore,String tname){
super(tname);
this.semaphore=semaphore;
}
public void run(){
try {
//tryAcquire尝试在500毫秒内获取执行权,并返回true/false反馈是否成功
boolean b = semaphore.tryAcquire(500, TimeUnit.MILLISECONDS);
//获取成功
if(b){
//执行任务
log.info("{}线程在{}时间执行任务",Thread.currentThread().getName(),System.currentTimeMillis());
Thread.sleep(200);
//执行完任务需要释放权利,以供其他线程拿到执行权
semaphore.release();
//获取失败
}else{
//执行降级任务
degrade();
}
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 定义降级任务
*/
private void degrade(){
log.info("降级服务");
}
}
2、模拟线程并发测试结果
public class TestMyTask {
public static void main(String[] args) {
//初始值为几?(就是限制几个线程同时执行,并行)
Semaphore semaphore = new Semaphore(3);
//模拟20个线程去抢执行权
for (int i = 0; i < 20; i++) {
new Thread(new DegradeTask(semaphore,"线程"+i)).start();
}
}
}
测试结果
[Thread-2] INFO testSemaphore.DegradeTask - Thread-2线程在1625380595501时间执行任务
[Thread-1] INFO testSemaphore.DegradeTask - Thread-1线程在1625380595501时间执行任务
[Thread-0] INFO testSemaphore.DegradeTask - Thread-0线程在1625380595501时间执行任务
[Thread-4] INFO testSemaphore.DegradeTask - Thread-4线程在1625380595709时间执行任务
[Thread-5] INFO testSemaphore.DegradeTask - Thread-5线程在1625380595709时间执行任务
[Thread-3] INFO testSemaphore.DegradeTask - Thread-3线程在1625380595709时间执行任务
[Thread-6] INFO testSemaphore.DegradeTask - Thread-6线程在1625380595910时间执行任务
[Thread-8] INFO testSemaphore.DegradeTask - Thread-8线程在1625380595910时间执行任务
[Thread-7] INFO testSemaphore.DegradeTask - Thread-7线程在1625380595910时间执行任务
[Thread-9] INFO testSemaphore.DegradeTask - 降级服务
[Thread-10] INFO testSemaphore.DegradeTask - 降级服务
[Thread-17] INFO testSemaphore.DegradeTask - 降级服务
[Thread-13] INFO testSemaphore.DegradeTask - 降级服务
[Thread-18] INFO testSemaphore.DegradeTask - 降级服务
[Thread-16] INFO testSemaphore.DegradeTask - 降级服务
[Thread-15] INFO testSemaphore.DegradeTask - 降级服务
[Thread-14] INFO testSemaphore.DegradeTask - 降级服务
[Thread-12] INFO testSemaphore.DegradeTask - 降级服务
[Thread-11] INFO testSemaphore.DegradeTask - 降级服务
[Thread-19] INFO testSemaphore.DegradeTask - 降级服务
semaphore源码解析
- 每个线程必须从semaphore获得一个许可,当线程执行完后,许可将被返回到池中,并向semaphore返回一个许可,允许另一个线程获得该许可。
- 请注意,在调用semaphore的acquire时不会持有同步锁,因为这将阻止将许可返回到池中。semaphore封装了限制对池的访问所需的同步,与维护池本身一致性所需的任何同步是分开的。
- 一个初始化为1的semaphore,它最多只有一个可用的许可,就可以作为一个互斥锁。这通常被称为二进制信号量,因为它只有两种状态:一种允许可用,或者不允许。当以这种方式使用时,二进制信号量具有一个属性(不像许多java.util.concurrent.lock Lock实现),即“锁”可以由除所有者以外的线程释放(因为信号量没有所有权的概念)。这在某些特定的上下文中可能很有用,比如死锁恢复。
- 这个类的构造函数可选地接受公平性参数。
- 当设置为false时,该类不保证线程获取许可的顺序。特别地,是允许阻塞的,也就是说,一个调用acquire的线程可以在一个正在等待的线程之前被分配一个许可证——逻辑上,新线程将自己放在等待线程队列的头部。
- 当公平性设置为true时,信号量保证调用任何获取方法的线程会被选择,以其调用这些方法的顺序获得许可(先入先出;先进先出)。请注意,FIFO顺序必然适用于这些方法中特定的内部执行点。因此,一个线程可能在另一个线程之前调用acquire,但在另一个线程之后到达排序点,从方法返回时也是如此。还要注意,非定时的tryAcquire方法不遵守公平性设置,但会接受任何可用的许可。
- 通常,用于控制资源访问的信号量应该初始化为公平的,以确保没有线程因为访问资源而耗尽。当将信号量用于其他类型的同步控制时,非公平排序的吞吐量优势往往超过公平性考虑。
- 该类还提供了方便的方法来一次获取和释放多个许可。当这些方法在不确定公平的情况下使用时,要注意增加的无限期推迟的风险。
static final class FairSync extends Sync {
private static final long serialVersionUID = 2014338818796000944L;
FairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors())
return -1;
//*********state控制许可数量从而控制线程执行数量***开始***//
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
//*********state控制许可数量从而控制线程执行数量***结束***//
}
}
}
更多推荐
已为社区贡献1条内容
所有评论(0)