Semaphore信号量
Semaphore 是一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。
Semaphore 是java.util.concurrent包下面的一个类,主要用于阻塞线程,合理调度线程的效果。
先上demo代码:
package com.java.concurrent.semaphore;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
public class SemaphoreDemo {
private final Semaphore semaphore;
private boolean[] resourceArray;
private final ReentrantLock lock;
private final int size = 1;
public SemaphoreDemo() {
semaphore = new Semaphore(size,true);
resourceArray = new boolean[size];
lock = new ReentrantLock(true);
}
public void use(){
try {
semaphore.acquire();
int index = get();
if(index!=-1){
System.out.println("index : "+index+",in...");
Thread.sleep(1000);
resourceArray[index]=false;
System.out.println("index : "+index+",out.");
}else{
System.out.println("index : "+index);
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
}
public int get(){
int index = -1;
lock.lock();
try{
for(int i=0;i<size;i++){
if(!resourceArray[i]){
resourceArray[i] = true;
index = i;
break;
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
return index;
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
SemaphoreDemo semaphoreDemo = new SemaphoreDemo();
for(int i=0;i<3;i++){
executorService.submit(()->{
semaphoreDemo.use();
});
}
}
}
执行结果:
index : 0,in…
index : 0,out.
index : 0,in…
index : 0,out.
index : 0,in…
index : 0,out.
显然,可以看到在结果中在线程数大于size的时候,并不会只执行一次。由于 semaphore.acquire(); 这个代码的存在,会将其余线程阻塞起来,并公平的调用。接下来看看Semaphore的源码。
package java.util.concurrent;
public class Semaphore implements java.io.Serializable {
//省略···
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
//省略···
}
通过acquire()作为入口,可以看到内部调用了sync.acquireSharedInterruptibly(1);
//sync实现了AbstractQueuedSynchronizer这个抽象类,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;
}
}
}
//并且实现了公平锁和非公平锁
/**
* NonFair version
*/
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);
}
}
/**
* Fair version
*/
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;
}
}
}
由刚才的sync.acquireSharedInterruptibly(1);进入
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
//如果线程被打断,则抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//执行至少一次tryAcquireShared()
if (tryAcquireShared(arg) < 0)
//如果这里返回小于0,就会放到aqs的队列中排队,重复获取许可,否则一直循环直到被打断
doAcquireSharedInterruptibly(arg);
}
由于我们创建的时候用的是公平锁,所以看一下公平锁的实现
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;
//如果许可大于等于0则执行后面compareAndSetState方法,
//cas原子操作,可以模仿一下||写法,简单又暴力
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
}
回看上面挂起到aqs的方法
/**
* Acquires in shared interruptible mode.
* @param arg the acquire argument
*/
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
//放到等待队列里面
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
//无限循环,等同于while
for (;;) {
//获取前一个结点
final Node p = node.predecessor();
if (p == head) {
//重复获取排队信息
int r = tryAcquireShared(arg);
if (r >= 0) {
//获取到许可的话,去掉引用方便gc,return出去
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
//除非线程被取消或者打断,否则一直循环
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
到这里基本结束,另外可以思考的点,java还有什么通讯方式,通讯方式是怎么样的