并发编程-Semaphore的用法

前言

继续看并发编程里的基础知识,今天来看一下Semaphore,Semaphore又称信号量,同样也是CyclicBarrierjava.util.concurrent包下一个多线程交互的辅助工具类。

Semaphore的概念

用来控制线程并发的数量。有点像lock,实现对资源获取权的限制,又有点像线程池,有多个资源获取权供线程获取和释放,不同的是线程通过Semaphore加锁和获取释放的并不是资源本身,而是Semaphore提供的一种许可状态。可能这么说不大好理解,我们来看看Semaphore的用法

Semaphore的用法

也是提供了两个构造函数

1
2
3
4
//通过permits指定控制并发线程的个数
public Semaphore(int permits) {}
//通过fair,指定是否采用先进先出的策略来获取信号量许可,默认是NOfair的
public Semaphore(int permits, boolean fair){}

核心方法:

1
2
3
4
//获取一个信号量许可,对应的还有获取多个信号量的重载方法,若获取不到,当前线程阻塞
public void acquire() throws InterruptedException {}
//释放一个信号量许可,对应的还有释放多个信号量的重载方法
public void release(){}

看一个例子,通过Semaphore实现十个线程只有五个可以并发的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(5);
for (int i = 0; i < 10; i++) {
new Worker(semaphore).start();
}
}
static class Worker extends Thread {
private Semaphore semaphore;
public Worker(Semaphore semaphore) {
this.semaphore = semaphore;
}
public void run() {
try {
semaphore.acquire();
System.out.println("子线程" + Thread.currentThread().getName() + "开始执行");
Thread.sleep(3000);
System.out.println("子线程" + Thread.currentThread().getName() + "执行完毕");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

执行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
子线程Thread-0开始执行
子线程Thread-1开始执行
子线程Thread-2开始执行
子线程Thread-3开始执行
子线程Thread-4开始执行
子线程Thread-1执行完毕
子线程Thread-5开始执行
子线程Thread-2执行完毕
子线程Thread-6开始执行
子线程Thread-4执行完毕
子线程Thread-7开始执行
子线程Thread-0执行完毕
子线程Thread-9开始执行
子线程Thread-3执行完毕
子线程Thread-8开始执行
子线程Thread-5执行完毕
子线程Thread-6执行完毕
子线程Thread-9执行完毕
子线程Thread-7执行完毕
子线程Thread-8执行完毕

从结果可以看到前五个线程并发执行,后五个必须等前面五个释放信号量许可才能开始执行
在有些场景下为了避免限制并发之后,大量线程因调用semaphore.acquire()而出现死等的情况,我们可以通过判断其提供的非阻塞的几个方法的返回结果来避免线程等待:

1
2
3
4
5
6
7
8
//尝试获取一个信号量许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire() { };
//尝试获取一个信号量许可,若在指定的时间内获取成功,则立即返回true,则立即返回false
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { };
//尝试获取permits个信号量许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire(int permits) { };
//尝试获取permits个信号量许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { };

由此我们可以看出,通过Semaphore我们可以控制资源的访问权限,比如连接池。

坚持原创技术分享,您的支持将鼓励我继续创作!