[Java 并发编程] 20. Semaphores

2020-09-04

Semaphores(信号)是一个线程同步结构,它既可以用于线程通信以避免丢失信号,也可以像锁一样使关键代码(介于lock和unlock之间的代码)在多个线程之间同步运行。

Java 5 提供了 java.util.concurrent.Semaphore 类,所以你可以不需要自己实现 Semaphore,但我们需要知道它的原理并使用它。

1. 简单的 Semaphore

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @author : sungm
* @date : 2020-09-04 15:38
*/
public class Semaphore {

private boolean signal = false;

public synchronized void take() {
this.signal = true;
this.notify();
}

public synchronized void release() throws InterruptedException {
while (this.signal) {
this.wait();
}
this.signal = false;
}

}

take() 方法发送一个信号,release() 方法等待信号。

使用 Semaphore 可以避免丢失信号。 我们可以使用 take() 方法代替 notify(),使用 release() 方法代替 wait()。如果 take() 方法的调用发生在 release() 方法之前,信号标志 signal = true 被保存下来,等到release() 方法被调用时便不满足自旋条件,不进入wait() 方法,因此而避免了信号丢失的情况发生。


2. 使用 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
/**
* @author : sungm
* @date : 2020-09-04 15:50
*/
public class Main {

public static void main(String[] args) {
Semaphore semaphore = new Semaphore();

//发送信号的线程
Thread sendingThread = new Thread(semaphore::take, "SendingThread");

//接收信号的线程
Thread receivingThread = new Thread(() -> {
try {
semaphore.release();
} catch (Exception e) {

}
}, "ReceivingThread");

sendingThread.start();
receivingThread.start();
}
}

3. Semaphore 计数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @author : sungm
* @date : 2020-09-04 15:38
*/
public class Semaphore {

private int signal = 0;

public synchronized void take() {
this.signal++;
this.notify();
}

public synchronized void release() throws InterruptedException {
while (this.signal != 0) {
this.wait();
}
this.signal--;
}

}

4. 有限 Semaphore

Semaphore 计数没有设置信号上限,我们可以给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
27
28
29
30
31
32
33
34
35
/**
* @author : sungm
* @date : 2020-09-04 16:00
*/
public class BoundedSemaphore {

private int signal = 0;
private int bound = 0;

/**
* 构造函数注入 Semaphore 上限
*
* @param bound 界限(signal的最大值)
*/
public BoundedSemaphore(int bound) {
this.bound = bound;
}

public synchronized void take() throws InterruptedException {
while (this.signal == this.bound) {
this.wait();
}
this.signal++;
this.notify();
}

public synchronized void release() throws InterruptedException {
while (this.signal == 0) {
this.wait();
}
this.signal--;
this.notify();
}

}

当发送的信号已经达到上限,那么发送信号的线程会进入等待,直到接收信号的线程消耗了一个信号,等待中的发送信号线程才可能被唤醒。


5. Semaphore 当成Lock使用

1
2
3
4
5
6
semaphore.take();
try {
//同步代码
} finally {
semaphore.release();
}