1. 信号量Semaphore的介绍
信号量可以看做对锁的扩展,我们知道的synchronized 和 重入锁 ReentrantLock ,一次只允许一个县城进行临界区资源访问,而信号量可以使多个线程同时访问某一资源。
如在营业厅办理手机卡业务,只有有限个窗口来同时办理业务。如果所有窗口都在办理业务中,那么这时过来办理业务的人需要等待,直到有人办理完并通知下一位时,这个人才可以开始自己的业务的办理。在这期间,这个人需要一直等待。在这里负责通知下一位人员继续办理业务的系统(或者人)就起到了信号量(非负数)的作用,表示公共资源(窗口)的可用数量,当一个线程要使用公共资源时,需要查看信号量是否大于0,如果大于0,则将其减1,并取占用窗口。如果为0,线程会阻塞,知道有其他线程释放资源。
我们先来熟悉一下这个类的主要功能性方法:
public Semaphore(int permits){...}
public Semaphore(int permits, boolean fair) {...} // 第二个参数指定公平与否
public void acquire() throws InterruptedException {...} // 获取资源,获取不到会一直阻塞
public void acquireUninterruptibly() {...} // 同acquire(),但不会响应中断
public boolean tryAcquire() {...} // 尝试获取资源,获取不到直接返回false
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException {...}
// 等待指定时间来获取资源,获取不到返回false
public void release() {...} // 释放资源
public void acquire(int permits) throws InterruptedException {...} // 获取指定数量的资源,获取不到会一直阻塞
public void acquireUninterruptibly(int permits) {...} // 同acquire(int permits),但不会响应中断
public boolean tryAcquire(int permits) {...}
public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
throws InterruptedException {...}
public void release(int permits) {...}
public int availablePermits() {...} // 返回可使用资源数量
public int drainPermits() {...}
protected void reducePermits(int reduction) {...}
public boolean isFair() {...}
public final boolean hasQueuedThreads() {...}
public final int getQueueLength() {...}
protected Collection<Thread> getQueuedThreads() {...}
public String toString() {...}
使用最多的是方法acquire() 和release() 。
2. acquire() 和release()源码分析
在Java的并发包中,Semaphore类表示信号量。Semaphore内部主要通过AQS
(AbstractQueuedSynchronizer)实现线程的管理。Semaphore有两个构造函数,参数permits表示许可数,它最后传递给了AQS的state值。线程在运行时首先获取许可,如果成功,许可数就减1,线程运行,当线程运行结束就释放许可,许可数就加1。如果许可数为0,则获取失败,线程位于AQS的等待队列中,它会被其它释放许可的线程唤醒。在创建Semaphore对象的时候还可以指定它的公平性。一般常用非公平的信号量,非公平信号量是指在获取许可时先尝试获取许可,而不必关心是否已有需要获取许可的线程位于等待队列中,如果获取失败,才会入列。而公平的信号量在获取许可时首先要查看等待队列中是否已有线程,如果有则入列。
Semaphore内部类:
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;
}
}
}
acquire源代码
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) { // 这里就是阻塞发生的地方
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
可以看出,如果remaining <0 即获取许可后,许可数小于0,则获取失败,在doAcquireSharedInterruptibly方法中线程会将自身阻塞,然后入列。
release源代码
public void release() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
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;
}
}
可以看出释放许可就是将AQS中state的值加1。然后通过doReleaseShared唤醒等待队列的第一个节点。可以看出Semaphore使用的是AQS的共享模式,等待队列中的第一个节点,如果第一个节点成功获取许可,又会唤醒下一个节点,以此类推。
3. 使用示例
示例一:
package somhu;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemaphoreTest implements Runnable{
private Semaphore semaphore = new Semaphore(5);
@Override
public void run() {
try {
semaphore.acquire();
Thread.sleep(2000); // 模拟耗时操作
System.err.println(Thread.currentThread().getName() + "--完成任务!");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ExecutorService exe = Executors.newFixedThreadPool(20);
SemaphoreTest task = new SemaphoreTest();
for (int i = 0; i < 20; i++) {
exe.submit(task);
}
exe.shutdown();
}
}
示例二:
package javalearning;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemaphoreDemo {
private Semaphore smp = new Semaphore(3);
private Random rnd = new Random();
class TaskDemo implements Runnable{
private String id;
TaskDemo(String id){
this.id = id;
}
@Override
public void run(){
try {
smp.acquire();
System.out.println("Thread " + id + " is working");
Thread.sleep(rnd.nextInt(1000));
smp.release();
System.out.println("Thread " + id + " is over");
} catch (InterruptedException e) {
}
}
}
public static void main(String[] args){
SemaphoreDemo semaphoreDemo = new SemaphoreDemo();
//注意我创建的线程池类型,
ExecutorService se = Executors.newCachedThreadPool();
se.submit(semaphoreDemo.new TaskDemo("a"));
se.submit(semaphoreDemo.new TaskDemo("b"));
se.submit(semaphoreDemo.new TaskDemo("c"));
se.submit(semaphoreDemo.new TaskDemo("d"));
se.submit(semaphoreDemo.new TaskDemo("e"));
se.submit(semaphoreDemo.new TaskDemo("f"));
se.shutdown();
}
}
运行结果
Thread c is working
Thread b is working
Thread a is working
Thread c is over
Thread d is working
Thread b is over
Thread e is working
Thread a is over
Thread f is working
Thread d is over
Thread e is over
Thread f is over
可以看出,最多同时有三个线程并发执行,也可以认为有三个公共资源(比如计算机的三个串口)。