Semaphore 类的作用是什么?请举例说明
参考回答
Semaphore
是 Java 并发包中用于控制同时访问某个资源的线程数量的类。它提供了一个计数信号量,允许一组线程以受限的方式访问共享资源,确保资源不会被过多线程同时使用。通过 Semaphore
,可以实现线程的流量控制。
详细讲解与拓展
1. Semaphore 的作用
- 限制并发访问数量:Semaphore 是一种计数器,用来限制能够同时访问某个资源的线程数量。
-
实现许可证模型:
- 每个线程在访问资源前,需要从
Semaphore
中获取一个许可(通过调用acquire()
)。
- 每个线程在访问资源前,需要从
- 使用完资源后,线程需要释放许可(通过调用
release()
)。
2. Semaphore 的构造方法
Semaphore(int permits)
:创建一个信号量,指定最多允许的许可数量。-
Semaphore(int permits, boolean fair)
:- 如果
fair
参数为true
,信号量会以公平的方式分配许可(按照线程请求的先后顺序)。 - 如果
fair
参数为false
(默认),可能会出现“抢占”现象。
- 如果
3. Semaphore 的常用方法
acquire()
:获取一个许可,如果当前没有许可可用,则线程阻塞。release()
:释放一个许可,将其归还给信号量。tryAcquire()
:尝试获取一个许可,如果没有许可可用,立即返回false
。availablePermits()
:返回当前可用的许可数量。
4. 示例程序
场景:限制对打印机的并发访问
假设有 3 台打印机,但有多个线程需要使用打印机。通过 Semaphore
限制最多只有 3 个线程能够同时访问打印机。
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
public static void main(String[] args) {
// 创建一个信号量,最多允许 3 个线程同时访问
Semaphore semaphore = new Semaphore(3);
// 创建并启动 6 个线程
for (int i = 1; i <= 6; i++) {
new Thread(new PrinterTask(semaphore, "Thread-" + i)).start();
}
}
}
class PrinterTask implements Runnable {
private Semaphore semaphore;
private String threadName;
public PrinterTask(Semaphore semaphore, String threadName) {
this.semaphore = semaphore;
this.threadName = threadName;
}
@Override
public void run() {
try {
System.out.println(threadName + " is waiting for a printer...");
semaphore.acquire(); // 获取许可,可能会阻塞
System.out.println(threadName + " is using a printer...");
Thread.sleep((long) (Math.random() * 3000)); // 模拟打印时间
System.out.println(threadName + " is done printing.");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(); // 释放许可
System.out.println(threadName + " released a printer.");
}
}
}
运行结果(示例)
输出顺序可能因线程调度不同而变化:
Thread-1 is waiting for a printer...
Thread-2 is waiting for a printer...
Thread-3 is waiting for a printer...
Thread-1 is using a printer...
Thread-2 is using a printer...
Thread-3 is using a printer...
Thread-4 is waiting for a printer...
Thread-5 is waiting for a printer...
Thread-6 is waiting for a printer...
Thread-1 is done printing.
Thread-1 released a printer.
Thread-4 is using a printer...
Thread-2 is done printing.
Thread-2 released a printer.
Thread-5 is using a printer...
Thread-3 is done printing.
Thread-3 released a printer.
Thread-6 is using a printer...
Thread-4 is done printing.
Thread-4 released a printer.
Thread-5 is done printing.
Thread-5 released a printer.
Thread-6 is done printing.
Thread-6 released a printer.
5. 代码解析
- 信号量限制并发访问数量:
Semaphore semaphore = new Semaphore(3);
:创建一个最多允许 3 个线程同时访问的信号量。- 超过 3 个的线程会阻塞,直到有线程释放许可。
- 线程之间的协作:
- 当线程调用
semaphore.acquire()
时,如果没有可用许可,则线程阻塞。 - 当线程调用
semaphore.release()
时,归还许可,其他阻塞的线程会被唤醒。
- 当线程调用
- 资源分配的灵活性:
- 我们可以根据资源数量调整信号量的初始许可数。
6. 扩展:公平信号量
默认情况下,信号量是非公平的,即等待线程可能会被随机唤醒。如果需要按线程请求许可的顺序唤醒,可以使用公平信号量:
Semaphore semaphore = new Semaphore(3, true); // 创建一个公平信号量
公平信号量会按照线程的调用顺序唤醒阻塞线程,适合需要严格控制访问顺序的场景。
7. 应用场景
- 连接池: 控制线程获取数据库连接的数量,避免连接池耗尽。
- 流量控制: 限制同时访问某个服务的线程数量,例如限流。
- 多线程资源共享: 控制对文件、打印机等共享资源的访问。