在面试的时候被问到关于java信号量,并没有很了解这个并发同步器,这里学习之后做一点简单的记录。Semaphore我们通常称之为信号量,这是用来控制同时访问某个资源的线程数量的一个同步器。比如数据库连接,如果数据库连接的数量只有10个,但是此时在执行的线程数量大于10个,那么线程会报错,获取不到数据库连接。为了防止这样的情况,我们可以通过信号量Semaphore来进行流量的控制。
使用Semaphore控制并发流量
1 | package com.souche.study; |
Semaphore API介绍
Semaphore对每一个进入获取令牌的线程,都会尝试给予令牌,但是如果出现了供不应求的情况,每个线程需要的处理方式可能是不同的:
semaphore.acquire()
线程选择直接获取(非阻塞),进入等待队列,循环等待没获取到线程挂起。
通过源码分析在底层的具体实现:
1 | private void doAcquireSharedInterruptibly(int arg) |
#####
semaphore.tryAcquire()
线程可以选择等待一段时间(非阻塞),如果超时获取不到返回false,检测到中断信号线程中断。
贴上源码的实现:
1 | private boolean doAcquireSharedNanos(int arg, long nanosTimeout) |
semaphore.acquireUninterruptibly()
获取锁的过程中不允许中断,线程会一直处于获取锁的状态,只会在失败之后将标志位置为true。
贴上源码的实现:
1 | /** |
Semaphore的两种实现(公平or非公平)
Semaphore信号量提供了两种实现,公平和非公平的方式,这个可以类比retrantlock的公平非公平机制。一个会加入等待队列,一个不会加入等待队列。我们看下两个的源码:
1 | final int nonfairTryAcquireShared(int acquires) { |
1 | protected int tryAcquireShared(int acquires) { |
我们看到公平和非公平的方式只是多了一个方法,那么看下这个方法到底实现了什么:
1 | * true} if there is a queued thread preceding the { |
总结
在面试的时候被问到信号量当时也是蛮懵逼的,觉得自己深入了解过AQS队列同步器,但是对java原生实现的一些同步器了解并不是特别多。信号量这个名字并不是特别好理解,可以更形象的称之为令牌管理器。在并发线程比较多的时候,一些有限的资源类似数据库连接数不能及时供应,那么我们需要进行一个流量控制。对于客户端的请求来说,可以在一段时间获取不到这个令牌之后选择不再等待,也可以在获取令牌的方法一直等待直到被中断,或者干脆一直等下去,死磕。网上搜索的时候发现一篇介绍同样内容的[博客]还不错,可以mark下https://blog.csdn.net/hanchao5272/article/details/79780045。