Semaphore
(信号量)是Java并发工具包(java.util.concurrent
)中提供的一个同步辅助类,用于控制对某个资源的访问数量。它允许一个或多个线程同时访问某个特定的资源或资源池,但是可以通过设置许可的数量来限制并发访问的数量。
基本工作原理
Semaphore
的工作原理是基于许可的概念。在创建Semaphore
对象时,你需要指定一个许可的数量,这个数量表示同时能够访问资源的线程数。每个线程在访问资源之前,必须先获得一个许可(通过调用acquire()
方法)。如果许可的数量为零,则线程将被阻塞,直到其他线程释放许可(通过调用release()
方法)。
主要方法
Semaphore(int permits)
:构造一个Semaphore
,并设置初始的许可数量。void acquire()
:从Semaphore
获取一个许可,如果当前没有可用的许可,则当前线程将被阻塞,直到有许可可用。void release()
:释放一个许可,将其返回给Semaphore
。如果有其他线程因为调用acquire()
方法而被阻塞,并且当前许可的数量小于最大许可数量,则释放许可可能会唤醒一个被阻塞的线程。tryAcquire()
:尝试从Semaphore
获取一个许可,如果当前有可用的许可,则返回true
,并将许可的计数减一;如果当前没有可用的许可,则返回false
,并且当前线程不会被阻塞。tryAcquire(long timeout, TimeUnit unit)
:在指定的时间内尝试从Semaphore
获取一个许可。如果在指定的时间内成功获取了许可,则返回true
;如果在指定的时间内没有获取到许可,则返回false
。
使用示例
以下是一个简单的示例,展示了如何使用Semaphore
来控制对某个资源的并发访问:
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
private static final int MAX_PERMITS = 3; // 最大许可数量
private final Semaphore semaphore;
public SemaphoreExample() {
this.semaphore = new Semaphore(MAX_PERMITS);
}
public void accessResource(String threadName) {
try {
// 获取许可
semaphore.acquire();
// 模拟资源访问
System.out.println(threadName + " 获取到许可,正在访问资源...");
Thread.sleep(2000); // 模拟资源访问时间
System.out.println(threadName + " 访问资源完毕,释放许可...");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 释放许可
semaphore.release();
}
}
public static void main(String[] args) {
SemaphoreExample example = new SemaphoreExample();
for (int i = 0; i < 5; i++) {
new Thread(() -> example.accessResource(Thread.currentThread().getName())).start();
}
}
}
运行结果
Thread-0 获取到许可,正在访问资源...
Thread-1 获取到许可,正在访问资源...
Thread-2 获取到许可,正在访问资源...
(等待一段时间后)
Thread-0 访问资源完毕,释放许可...
Thread-3 获取到许可,正在访问资源...
(再等待一段时间后)
Thread-1 访问资源完毕,释放许可...
Thread-4 获取到许可,正在访问资源...
(最后)
Thread-2 访问资源完毕,释放许可...
Thread-3 访问资源完毕,释放许可...
Thread-4 访问资源完毕,释放许可...
注意事项
Semaphore
可以用于保护一个或多个共享资源的访问,确保在任何时候都不会有超过指定数量的线程同时访问这些资源。Semaphore
是线程安全的,可以在多个线程中安全地使用。- 与
CountDownLatch
不同,Semaphore
的许可可以被释放和重新获取,因此它适用于需要多次协调资源访问的场景。 - 在使用
Semaphore
时,需要确保在最终释放资源时调用release()
方法,以避免线程永久阻塞。