什么是Semaphore?
Semaphore(信号量)是用于控制 同时访问特定资源的线程的数量,它通过协调各个线程,保证合理的使用公共资源。
线程可以通过acquire()方法来获取信号量的许可,当信号量中没有可用的许可的时候,线程阻塞,直到有可用的许可为止。线程可以通过release()方法释放它持有的信号量的许可。
怎么用Semaphore?
代码如下:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
//模拟的线程数量
private final static int threadCount = 20;
public static void main(String[] args) throws Exception {
//线程池
ExecutorService exec = Executors.newCachedThreadPool();
//同时允许3个线程执行
final Semaphore semaphore = new Semaphore(3);
//通过lambda表达式创建线程,同时添加进入线程池中
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() -> {
try {
semaphore.acquire(); // 获取一个许可
test(threadNum);
semaphore.release(); // 释放一个许可
} catch (Exception e) {
e.printStackTrace();
}
});
}
exec.shutdown();
}
private static void test(int threadNum) throws Exception {
System.out.println(threadNum);
Thread.sleep(1000);
}
}
如代码所示,在new Semaphore(3),向里面传递了一个整数3,表示同时允许3个线程运行。当运行到semaphore.acquire()时,会去请求一个许可,默认不传参的情况下是一个许可:
当然你可以向里面传入一个正整数,比如3,那么就会向外发放3个许可。
需要注意的三点是:
- permis必须是正整数
- permis必须小于new Semaphore()时传入的数字。
如果大于,程序则会因为没有将许可发放完毕而一直处于等待转态。这是因为semaphone在发放许可时,必须等到指定的许可数量发放完毕,才会让得到许可的线程执行。
例如:在new Semaphore()时传值3,而acquire()时传值4,那么接下来semaphore会放行3个线程,等到了发放许可的时候,会将4个许可中的3个发放给被放行的3个线程,而第4个许可却迟迟发不出去了,这时便会陷入僵局之中,外面的线程想进去,里面的线程出不去。 - 向acquire()和release()中传递的值最好相等,否则可能会出错
使用acquire()方法的线程在没有得到许可的情况下,会处于阻塞状态,直到获取许可为止。
与此不同的是tryAcquire()方法,使用该方法的线程会去尝试获取许可,当没有获取许可时,线程会被抛弃。
不过可以设置等待时间,在等待时间内获取到许可,线程就不会被抛弃了。