信号量的作用在于限制应用中某个部分的并发量,控制流量。
Semaphore的构造方法里会传入一个int类型的参数
/*** Creates a {@code Semaphore} with the given number of* permits and nonfair fairness setting.** @param permits the initial number of permits available.* This value may be negative, in which case releases* must occur before any acquires will be granted.*/public Semaphore(int permits) {sync = new NonfairSync(permits);}
从注释可以看出,permits参数可以为负数,这种情况下释放资源(releases)必须在申请资源(acquires)之前调用。
/*** 从信号量中获得一个许可,线程阻塞直到获取到许可,或者线程被关闭* 如果有一个许可是可用的,就获取到该许可,并使可用许可数目减少1*/public void acquire() throws InterruptedException {sync.acquireSharedInterruptibly(1);}/*** 释放一个许可,可用许可数量增1*/public void release() {sync.releaseShared(1);}
releases()方法和acquires()方法分别执行释放许可和申请许可的操作
使用
example 1:
public static void main(String[] args) throws InterruptedException {Semaphore semaphore = new Semaphore(3);ExecutorService executorService=Executors.newCachedThreadPool();for (int i=0;i<20;i++){executorService.execute(()->{try {semaphore.acquire();System.out.println(Thread.currentThread().getName()+" 进入!");Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}finally {semaphore.release();System.out.println(Thread.currentThread().getName()+" 释放!");}});}executorService.shutdown();}
20个线程都在请求信号量,但信号量只允许有3个线程进入,所以只有获得信号量的线程结束释放信号量后才能有新的线程进入。如果我们不释放信号呢,就只能打印出3句log了。
pool-1-thread-1 进入!pool-1-thread-5 进入!pool-1-thread-2 进入!
example 2:
ExecutorService executor = Executors.newFixedThreadPool(10);Semaphore semaphore = new Semaphore(5);Runnable longRunningTask = () -> {boolean permit = false;try {permit = semaphore.tryAcquire(1, TimeUnit.SECONDS);if (permit) {System.out.println("Semaphore acquired");sleep(5);} else {System.out.println("Could not acquire semaphore");}} catch (InterruptedException e) {throw new IllegalStateException(e);} finally {if (permit) {semaphore.release();}}}IntStream.range(0, 10).forEach(i -> executor.submit(longRunningTask));stop(executor);
执行器可能并发执行10个任务,但是我们设置信号量的大小为5,因此并发数被限制为5。切记要使用try/finally 代码块以释放信号量。
执行上述代码获得以下输出:
Semaphore acquiredSemaphore acquiredSemaphore acquiredSemaphore acquiredSemaphore acquiredCould not acquire semaphoreCould not acquire semaphoreCould not acquire semaphoreCould not acquire semaphoreCould not acquire semaphore
example 3:
Semaphore semaphore = new Semaphore(1);semaphore.acquire();try {......}finally {semaphore.release();}
如果将信号量的初始大小设置为1,那么它就是一个不可重入的互斥锁。
