Semaphore是一个计数器,在计数器不为0的时候对线程就放行,一旦达到0,那么所有请求资源的新线程都会被阻塞,包括增加请求到许可的线程,也就是说Semaphore不是可重入的。每一次请求一个许可都会导致计数器减少1,同样每次释放一个许可都会导致计数器增加1,一旦达到了0,新的许可请求线程将被挂起。
简单例子
public class SemaphoreTest {
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
Semaphore semaphore = new Semaphore(5);
for (int i = 0; i < 20; i++) {
exec.execute(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("test");
semaphore.release();//如果注释掉,则只会打印5个
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
exec.shutdown();
}
}
源码分析
构造函数
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
//fair设置是否公平
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
公平Semaphore 按照队列中的顺序分配Semaphore所管理的许可就是公平的。
非公平Semaphore 无论当前线程是不是队列的头部,它都会直接获取信号量。
可以看出Semaphore的实现依赖于NonfairSync和FairSync这两个类
Semaphore(int 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);
}
}
/**
* 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;
}
}
}
NonfairSync和FairSync都继承自Sync
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;
}
}
}
AbstractQueuedSynchronizer为实现依赖于先进先出 (FIFO) 等待队列的阻塞锁定和相关同步器(信号量、事件,等等)提供一个框架。
acquire()
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
直接调用的是AQS的acquireSharedInterruptibly(int arg)方法。
获取一个许可,如果没有则阻塞。
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
release()
释放一个许可
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
用Semophore实现生产消费模式
class Buffer{
List<Integer> bufferList = new ArrayList<>();
Semaphore producerSemp = new Semaphore(1);
Semaphore consumerSemp = new Semaphore(0);//要先生产,然后再消费,所以先设置为0
public int get(){
try {
consumerSemp.acquire();//从此信号量获取一个许可,在提供一个许可前一直将线程阻塞
return bufferList.remove(0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
producerSemp.release();//释放一个生产者的许可
}
return 0;
}
public void put(int num){
try {
producerSemp.acquire();//获得一个生产者的许可
bufferList.add(num);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
consumerSemp.release();//释放一个消费者的许可
}
}
}
class Consumer implements Runnable{
Buffer buffer;
public Consumer(Buffer buffer) {
this.buffer = buffer;
}
@Override
public void run() {
while(!Thread.interrupted()){
int num = buffer.get();
System.out.println("Consumer get: " + num);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer implements Runnable{
Buffer buffer;
int num = 0;
public Producer(Buffer buffer) {
this.buffer = buffer;
}
@Override
public void run() {
while(!Thread.interrupted()){
buffer.put(num);
System.out.println("Producer put " + num);
num++;
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ProducerConsumer {
public static void main(String[] args) {
Buffer buffer = new Buffer();
Producer producer = new Producer(buffer);
Consumer consumer = new Consumer(buffer);
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(producer);
executorService.execute(consumer);
executorService.shutdown();
}
}
输出:
Producer put 0
Consumer get: 0
Producer put 1
Consumer get: 1
Producer put 2
Consumer get: 2
Consumer get: 3
Producer put 3
Consumer get: 4
Producer put 4
Producer put 5
Consumer get: 5
Producer put 6
Consumer get: 6
...
在《Effective java》中的第69条
并发工具优先于wait和notify
java.util.concurrent中的工具分为三类
- Executor Framework
- 并发集合 Concurrent Collection
- 同步器 Synchronizer(最常用的为CountDownLatch 和 Semaphore)