1. 基本原理
-
许可计数器
Semaphore 在构造时设定一个许可数量,这个数量表示可以同时访问共享资源的线程数。- 如果许可数为 1,Semaphore 就相当于一个互斥锁;
- 如果许可数大于 1,就可以允许多个线程同时访问。
-
获取与释放许可
- 获取许可:线程调用
acquire()
方法时,会尝试从许可计数器中减去一个许可。如果当前没有足够的许可,线程将阻塞,直到有其他线程释放许可。 - 释放许可:线程使用完共享资源后,必须调用
release()
方法,将许可归还给 Semaphore,使得其他等待线程能够继续获取许可。
- 获取许可:线程调用
2. 使用方法
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
// 初始化 Semaphore,设置许可数为 3,表示最多允许 3 个线程同时访问共享资源
private static final Semaphore semaphore = new Semaphore(3);
private static int sharedResource = 0;
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(new Worker()).start();
}
}
static class Worker implements Runnable {
@Override
public void run() {
try {
// 获取许可,若没有可用许可,则阻塞等待
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 获取许可,开始访问资源。");
// 对共享资源进行操作(例如累加计数器)
sharedResource++;
Thread.sleep(2000); // 模拟操作耗时
System.out.println(Thread.currentThread().getName() + " 释放许可,结束访问资源。");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 释放许可,允许其他线程进入
semaphore.release();
}
}
}
}
在这个例子中,Semaphore 被初始化为 3,因此最多允许 3 个线程同时访问 sharedResource
。当第 4 个线程调用 acquire()
时,它将会阻塞,直到其他线程调用 release()
归还许可。
3. 特性与注意事项
-
公平性
Semaphore 支持公平和非公平两种模式。- 公平模式:线程获取许可时按照先入先出顺序进行,防止线程饥饿;
- 非公平模式:线程获取许可时不保证顺序,这通常能提供更高的吞吐量。
可以通过构造方法的第二个参数来指定是否为公平模式:
Semaphore semaphore = new Semaphore(3, true); // 公平模式
-
非重入性
-
资源池管理
Semaphore 常用于实现对有限资源的访问控制,通过许可的获取和释放来管理资源的并发访问。