Semaphore(信号灯)使用和源码
Semaphore(信号灯):一个计数信号量。顾名思义,一个信号量拥有一定数量的许可证,一个线程acquire需要申请到许可证才能获取锁,否则阻塞等待;释放的时候会返还许可证给阻塞的线程申请,常用于限制可以访问某些资源(物理或逻辑的)线程数目,限流场景等。
通过测试代码,看一下Semaphore的基本用法
public class Test { public static void main(String[] args) { int N = 8; //工人数 Semaphore semaphore = new Semaphore(5); //机器数目 for(int i=0;i<N;i++) new Worker(i,semaphore).start(); } static class Worker extends Thread{ private int num; private Semaphore semaphore; public Worker(int num,Semaphore semaphore){ this.num = num; this.semaphore = semaphore; } @Override public void run() { try { semaphore.acquire(); System.out.println("工人"+this.num+"占用一个机器在生产..."); Thread.sleep(2000); System.out.println("工人"+this.num+"释放出机器"); semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } } } }

结果明显彰显了信号灯的作用,然为什么它有这个功能呢?我们来看看Semaphore的源码
Semaphore的构造
//默认非公平
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
//自定义公平性
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
默认的构造为例
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);
}
}
super(permits)执行
Sync(int permits) {
//设置AQS中的state的值
setState(permits);
}
上面构造完成后,semaphore.acquire() 执行
代码最后到了AbstractQueuedSynchronizer(AQS)
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
tryAcquireShared方法 执行
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
//通过自旋,返回state减acquires的值
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
//通过cas改变它的值
compareAndSetState(available, remaining))
return remaining;
}
}
tryAcquireShared的结果如果小于0,代表位子已经被占满了,doAcquireSharedInterruptibly 执行
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
//增加到AQS队列中
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
//再次尝试获得位子
//如果信号等数为2,则代表2个位子
int r = tryAcquireShared(arg);
if (r >= 0) {
//进入if代表还有位子
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
//shouldParkAfterFailedAcquire
//代表Acquire失败后尝试去park线程
if (shouldParkAfterFailedAcquire(p, node) &&
//park线程
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
semaphore.release() 执行释放位子 代码执行到达
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
tryReleaseShared 执行
protected final boolean tryReleaseShared(int releases) {
for (;;) {
//目的就是把state加1
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
doReleaseShared 执行
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
//唤醒队列中park的线程
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
没有获取到位子的线程 ,重新开始自旋获取位子

如果锁(本人通俗叫做位子)竞争激烈,在非公平的情况下,当前线程有可能重新被挂起。
生活场景:读书的时候临考期间,很多人都会去图书馆占座,因为图书馆的位子是固定的,只有别人走了,你才能坐,你在没有位子的时候处于等待状态,这是如果有人起身离开,如果在公平的情况下,你就会拥有这个宝贵的座位,但是总有些人没有注意到你的存在,或者就是不守规矩,他本来再你后面来的就可能抢先一步拿到座位,这就是非公平,类比上面的Semaphore,就很容易理解它的原理了。
=========================================================================================================================================
我只是一粒简单的石子,未曾想掀起惊涛骇浪,也不愿随波逐流
每个人都很渺小,努力做自己,不虚度光阴,做真实的自己,无论是否到达目标点,既然选择了出发,便勇往直前
我不能保证所有的东西都是对的,但都是能力范围内的深思熟虑和反复斟酌

浙公网安备 33010602011771号