信号量是基于共享锁和许可的原理实现,在初始时给定许可数,获取时如果当前许可足够,就获得许可往下执行,否则就阻塞。
信号量还分公平信号量和非公平信号量,两者的区别就在于再尝试获取许可的时候,公平信号量还需要判断当前线程所在的节点是不是CLH队列的头节点,其他都一样。释放锁的过程两者也是一样的。
先看下用法:
package com.pzx.test.test00001;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Semaphore;
public class SemaphoreDemo {
static Semaphore semaphore = new Semaphore(10);
static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ");
public static void main(String[] args) {
ThreadA threadA = new ThreadA();
for (int i = 1; i <= 4; i++) {
Thread thread = new Thread(threadA, "t"+i);
threadA.setPermits(3);
thread.start();
}
}
static class ThreadA implements Runnable {
int permits;
@Override
public void run() {
int p = permits;
try {
semaphore.acquire(p);
System.out.println(sdf.format(new Date()) + Thread.currentThread().getName() + " acquired permit, available permits:" + semaphore.availablePermits() + " at now");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(p);
System.out.println(sdf.format(new Date()) + Thread.currentThread().getName() + " released permit, available permits:" + semaphore.availablePermits() + " at now");
}
}
public int getPermits() {
return permits;
}
public void setPermits(int permits) {
this.permits = permits;
}
}
}
输出:
2018-09-11 22:21:44 t3 acquired permit, available permits:1 at now
2018-09-11 22:21:44 t1 acquired permit, available permits:1 at now
2018-09-11 22:21:44 t2 acquired permit, available permits:1 at now //这里隔了5秒才往下执行,线程t4等了5秒才获取许可
2018-09-11 22:21:49 t3 released permit, available permits:7 at now
2018-09-11 22:21:49 t4 acquired permit, available permits:7 at now
2018-09-11 22:21:49 t1 released permit, available permits:7 at now
2018-09-11 22:21:49 t2 released permit, available permits:7 at now
2018-09-11 22:21:54 t4 released permit, available permits:10 at now
再看源码
主要就看下构造函数,acquire和release方法
// 抽象内部类Sync,又有两个公平与非公平的子类
public class Semaphore implements java.io.Serializable {
private static final long serialVersionUID = -3222578661600680210L;
/** All mechanics via AbstractQueuedSynchronizer subclass */
private final Sync sync;
// 继承AQS
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
// 构造函数就是设置许可数为state数
Sync(int permits) {
setState(permits);
}
final int getPermits() {
return getState();
}
// 非公平的获取锁
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
// 计算剩余的许可数
int remaining = available - acquires;
// 如果剩余的许可数<0,就返回这个负数
// 如果剩余许可数>=0,就设置state为剩余的数,设置成功才返回剩余的许可数,
// 否则重试
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");
// 如果设置成功就返回true,否则重试
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;
}
}
// 许可数置0
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);
}
}
// 公平版本的信号量,就是再获取共享锁时判断下当前线程是不是再CLH队列头
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;
}
}
}
// 构造函数
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
// 获取许可数
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public void acquire(int permits) throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits);
}
// AQS里面的代码
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 尝试获取共享锁,这是上面重写的方法,获取失败是返回负数
if (tryAcquireShared(arg) < 0)
// 实际去获取锁,如果没获取到就阻塞,等待其他许可释放的时候被唤醒
doAcquireSharedInterruptibly(arg);
}
// 释放许可
public void release() {
sync.releaseShared(1);
}
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
// AQS里面的释放共享锁
public final boolean releaseShared(int arg) {
// 尝试释放锁就再上面的Sync里有实现,尝试成功后就
if (tryReleaseShared(arg)) {
// 以共享模式释放锁,其实里面的操作主要目的是唤醒后继线程,也就是还没有获取许可,
// 阻塞在那里的线程
doReleaseShared();
return true;
}
return false;
}