Semaphore原理

简介: 文章概述了Semaphore的工作原理及其使用场景:Semaphore是一种有效的限流工具,可以用来控制同一时刻访问共享资源的线程数量。它适用于需要对资源访问进行限流的场景,以避免资源过载或实现公平的资源分配。

前言

    Semaphore是用于限制指定个数线程控制访问资源的同步工具,也是基于AQS实现的共享锁机制。它能控制每个时刻最多有多少个线程能访问共享资源,实现限流的功能。

使用效果

创建一个Semaphore,令牌数是3个,使用5个线程模拟获取令牌

java.util.concurrent.Semaphore semaphore = new java.util.concurrent.Semaphore(3);​        for (int i=0; i< 5; i++) {
   
               int finalI = i;            new Thread(new Runnable() {
   
                   @SneakyThrows                @Override                public void run() {
   
                       semaphore.acquire(1);                    System.out.println(Thread.currentThread().getName() + " 在"+ DateFormatUtils.format(new Date(),"yyyy/MM/dd HH:mm:ss") + " 申请到一个令牌");                    Thread.sleep((finalI +1) *3000);                    semaphore.release(1);                }            }).start();        }

控制台输出:

从日志发现,同时最多有3个线程获取到令牌,等释放令牌后,其他线程才能获取到令牌,起到限流的作用。

源码分析

AQS实现

abstract static class Sync extends AbstractQueuedSynchronizer {        private static final long serialVersionUID = 1192457210091910933L;​        Sync(int permits) {            setState(permits);        }​        final int getPermits() {            return getState();        }        //非公平方式获取共享锁,不排队,直接获取锁        final int nonfairTryAcquireShared(int acquires) {            for (;;) {                int available = getState();                int remaining = available - acquires;                if (remaining < 0 ||                    compareAndSetState(available, remaining))                    return remaining;            }        }​        //释放共享锁,归还令牌        protected final boolean tryReleaseShared(int releases) {            for (;;) {                int current = getState();                int next = current + releases;                if (next < current) // overflow                    throw new Error("Maximum permit count exceeded");                if (compareAndSetState(current, next))                    return true;            }        }        //减少令牌        final void reducePermits(int reductions) {            for (;;) {                int current = getState();                int next = current - reductions;                if (next > current) // underflow                    throw new Error("Permit count underflow");                if (compareAndSetState(current, next))                    return;            }        }        //令牌清零        final int drainPermits() {            for (;;) {                int current = getState();                if (current == 0 || compareAndSetState(current, 0))                    return current;            }        }    }        //非公平版本     static final class NonfairSync extends Sync {        private static final long serialVersionUID = -2694183684443567898L;​        NonfairSync(int permits) {            super(permits);        }​        protected int tryAcquireShared(int acquires) {            return nonfairTryAcquireShared(acquires);        }    }           /**     * 公平版本     */    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;                int available = getState();                int remaining = available - acquires;                if (remaining < 0 ||                    compareAndSetState(available, remaining))                    return remaining;            }        }    }

获取1个令牌:

public void acquire() throws InterruptedException {
   
           sync.acquireSharedInterruptibly(1);    }

获取多个令牌:

public void acquire(int permits) throws InterruptedException {
   
           if (permits < 0) throw new IllegalArgumentException();        sync.acquireSharedInterruptibly(permits);    }

释放一个令牌:

public void release() {
   
           sync.releaseShared(1);    }

释放多个令牌:

public void release(int permits) {
   
           if (permits < 0) throw new IllegalArgumentException();        sync.releaseShared(permits);    }

总结:

令牌获取,令牌释放是配对的,体现了令牌资源复用。它既支持公平获取令牌,也支持非公平获取令牌,可以根据实际业务场景选择,在接口限流场景中使用很多。

相关文章
|
2月前
|
Java
JAVA并发编程系列(7)Semaphore信号量剖析
腾讯T2面试,要求在3分钟内用不超过20行代码模拟地铁安检进站过程。题目设定10个安检口,100人排队,每人安检需5秒。实际中,这种题目主要考察并发编程能力,特别是多个线程如何共享有限资源。今天我们使用信号量(Semaphore)实现,限制同时进站的人数,并通过信号量控制排队和进站流程。并详细剖析信号量核心原理和源码。
|
3月前
|
Java 数据库 开发者
CountDownLatch、CyclicBarrier和Semaphore原理和区别
CountDownLatch、CyclicBarrier和Semaphore
|
4月前
|
数据库连接 数据库 Python
信号量(Semaphore)是一个同步工具
信号量(Semaphore)是一个同步工具
|
6月前
|
Java 数据库
Semaphore(信号量)源码解读与使用
Semaphore(信号量)源码解读与使用
|
6月前
|
算法 Java 调度
Semaphore实现原理全面解析
Semaphore(信号量)是一个同步工具类,通过Semaphore可以控制同时访问共享资源的线程个数。
Semaphore使用及原理解读
Semaphore使用及原理解读
Semaphore使用及原理解读
|
Java
Java多线程:Semaphore
Java多线程:Semaphore
116 0
|
Java 测试技术
Semaphore原理剖析
Semaphore原理剖析
145 0
并发编程之Semaphore信号量
`Semaphore` 翻译过来就是信号量, 其根本原理就是基于 `CAS` 共享锁的一种实现。举一个例子。 假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆不受阻碍的进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入一辆,如果又离开两辆,则又可以放入两辆,如此往复。
169 0
Semaphore 信号量源码分析
Semaphore 信号量源码分析
Semaphore 信号量源码分析