在Java中,信号量(Semaphore)是一种用于控制并发访问共享资源的同步机制。它的原理是维护一个计数器,这个计数器表示可以同时访问资源的线程数量。当线程请求访问资源时,信号量的计数器会减少;当线程释放资源时,计数器增加。如果计数器的值小于等于0,任何请求访问的线程都会被阻塞,直到有线程释放资源为止。
Semaphore的基本构造与使用
Java中提供了java.util.concurrent
包下的Semaphore
类来实现信号量机制。下面是Semaphore
的一些基本方法:
acquire()
: 使线程获取一个许可。如果没有可用的许可,调用线程会被阻塞。release()
: 释放一个许可,让其他线程可以获取。tryAcquire()
: 尝试获取一个许可,如果成功返回true
,否则返回false
,且不会阻塞。availablePermits()
: 返回当前可用的许可数量。
示例代码
下面的例子展示了如何使用Semaphore
来控制多个线程访问共享资源。假设我们有一个有限数量的数据库连接,使用信号量限制同时可以进行数据库操作的线程数。
import java.util.concurrent.Semaphore;
public class DatabaseConnection {
private static final int MAX_CONNECTIONS = 3; // 最大连接数
private static final Semaphore semaphore = new Semaphore(MAX_CONNECTIONS, true); // 信号量
public void accessDatabase(int threadNumber) {
try {
// 请求许可
System.out.println("Thread " + threadNumber + " is trying to access the database.");
semaphore.acquire(); // 如果没有许可,线程将被阻塞
System.out.println("Thread " + threadNumber + " has accessed the database.");
// 模拟数据库操作
Thread.sleep(2000); // 模拟操作耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Thread " + threadNumber + " was interrupted.");
} finally {
// 释放许可
System.out.println("Thread " + threadNumber + " is releasing the database.");
semaphore.release();
}
}
public static void main(String[] args) {
DatabaseConnection dbConnection = new DatabaseConnection();
for (int i = 1; i <= 10; i++) {
int threadNumber = i;
new Thread(() -> dbConnection.accessDatabase(threadNumber)).start();
}
}
}
代码解析
- 创建信号量:
semaphore
被初始化为MAX_CONNECTIONS
,这意味着最多允许3个线程同时访问数据库。 - 请求访问: 每个线程在访问数据库前调用
semaphore.acquire()
以请求一个许可。如果当前没有可用的许可,线程将被阻塞。 - 数据库操作: 一旦成功获取到许可,线程就可以进行数据库操作。这里我们用
Thread.sleep(2000)
模拟操作耗时。 - 释放许可: 操作完成后,线程必须调用
semaphore.release()
来释放许可,让其他线程可以继续请求访问。
总结
Java中的信号量是控制并发、限制对共享资源访问的重要机制。通过控制许可数量,可以有效地避免资源争用和线程过度竞争。在实现多线程应用时合理使用信号量,可以提高系统的性能和稳定性。