1. Java 中 Semaphore 的定义与原理
Semaphore
是 Java 并发包 java.util.concurrent
里的同步工具类,可用来控制同时访问特定资源的线程数量。其内部借助一个计数器来实现,创建 Semaphore
对象时需要指定计数器的初始值,此值代表可同时访问资源的线程数量上限。
线程在访问资源前需先调用 acquire()
方法获取许可,若此时计数器的值大于 0,计数器会减 1,线程获取许可并可以访问资源;若计数器的值为 0,线程会被阻塞,直到有其他线程释放许可。线程访问完资源后,需调用 release()
方法释放许可,这时计数器的值会加 1。
2. 作用
- 资源访问控制:对同时访问特定资源的线程数量进行限制,防止资源被过度使用。例如,在数据库连接池中,可使用
Semaphore
限制同时使用的数据库连接数量,避免数据库连接耗尽。 - 流量控制:在高并发场景下,可对进入系统的请求数量进行控制,防止系统因过多请求而崩溃。
3. 应用场景
3.1 数据库连接池
数据库连接是有限的资源,为避免过多线程同时请求数据库连接导致连接耗尽,可以使用 Semaphore
来控制并发访问数据库连接的线程数量。以下是示例代码:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.concurrent.Semaphore;
class DatabaseConnectionPool {
private static final int MAX_CONNECTIONS = 5;
private final Semaphore semaphore = new Semaphore(MAX_CONNECTIONS);
public Connection getConnection() throws InterruptedException, SQLException {
// 获取许可
semaphore.acquire();
try {
// 模拟获取数据库连接
return DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "username", "password");
} catch (SQLException e) {
// 若获取连接失败,释放许可
semaphore.release();
throw e;
}
}
public void releaseConnection(Connection connection) {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 释放许可
semaphore.release();
}
}
}
3.2 限流场景
在高并发的 Web 应用中,为防止过多请求同时涌入导致系统崩溃,可以使用 Semaphore
对请求进行限流。以下是简单示例:
import java.util.concurrent.Semaphore;
class RequestLimiter {
private static final int MAX_REQUESTS = 100;
private final Semaphore semaphore = new Semaphore(MAX_REQUESTS);
public boolean tryProcessRequest() {
// 尝试获取许可
return semaphore.tryAcquire();
}
public void releaseRequest() {
// 释放许可
semaphore.release();
}
}
3.3 多线程文件下载
在多线程文件下载场景中,为避免同时下载的文件数量过多导致网络带宽被占满,可以使用 Semaphore
控制同时下载的线程数量。以下是示例代码:
import java.util.concurrent.Semaphore;
class FileDownloader {
private static final int MAX_DOWNLOADS = 3;
private final Semaphore semaphore = new Semaphore(MAX_DOWNLOADS);
public void downloadFile(String url) {
try {
// 获取许可
semaphore.acquire();
System.out.println("开始下载文件: " + url);
// 模拟文件下载
Thread.sleep(2000);
System.out.println("文件下载完成: " + url);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放许可
semaphore.release();
}
}
}
通过这些例子可以看出,Semaphore
在需要控制并发访问资源数量的场景中非常有用。