概述
CountDownLatch(门闩)与CyclicBarrier(栅栏)看似需要等待线程数达到相同的数量才能继续执行,但它们并不能作为限流器使用,例如实现一个车辆限流,小于10辆直接通行,大于10辆则等待。CountDownLatch(门闩)与CyclicBarrier(栅栏)在小10辆时也无法通行,因此不适合作为限流器使用。Semaphore信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。
信号量模型
信号量模型还是很简单的,可以简单概括为:一个计数器,一个等待队列,三个方法。在信号量模型里,计数器和等待队列对外是透明的,所以只能通过信号量模型提供的三个方法来访问它们,这三个方法分别是:init()、down() 和 up()。你可以结合下图来形象化地理解。
这三个方法详细的语义具体如下所示。
init():设置计数器的初始值。
down():计数器的值减 1;如果此时计数器的值小于 0,则当前线程将被阻塞,否则当前线程可以继续执行。
up():计数器的值加 1;如果此时计数器的值小于或者等于 0,则唤醒等待队列中的一个线程,并将其从等待队列中移除。
使用方法
下面是 Java 代码的示例,acquire() 就是信号量里的 down() 操作,release() 就是信号量里的 up() 操作。
static int count;
//初始化信号量
static final Semaphore s
= new Semaphore(1);
//用信号量保证互斥
static void addOne() {
s.acquire();
try {
count+=1;
} finally {
s.release();
}
}
拓展实际应用:
public class T11_TestSemaphore {
static Semaphore semaphore = new Semaphore(2);
public static void main(String[] args){
new Thread(()->{
try {
semaphore.acquire();
System.out.println("1开始");
Thread.sleep(100);
semaphore.release();
System.out.println("1结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(()->{
try {
semaphore.acquire();
System.out.println("2开始");
Thread.sleep(100);
semaphore.release();
System.out.println("2结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}