JAVA同步工具类——Semaphore信号量

本文深入探讨了计数信号量(CountingSemaphore)的概念及应用,信号量是一种用于控制资源访问的重要同步工具,通过管理虚拟许可来限制同时访问资源的操作数量。文章详细解释了信号量的工作原理,包括许可的获取与释放过程,并通过一个体检室的实例展示了其在实际场景中的应用。

信号量

计数信号量(Counting Semaphore)用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量;

Semaphore管理着一组虚拟许可(permit),许可的初始数量可通过构造函数来指定;在执行操作时先获取许可(只要还有剩余的许可),使用完成后释放许可;如果没有许可那么acquire将阻塞直到有许可(或者被中断或操作超时);release方法将返回一个许可给信号量;

初始值为1的Semaphore称为二值信号量;二值信号量可以用作互斥体(mutex),并具备不可重入的加锁语义;谁拥有这个唯一的许可谁就拥有了互斥锁;

使用场景:

Semaphore是一件可以容纳N人的房间,如果人不满就可以进去,如果人满了,就要等待有人出来

/**
* 假设公司体检,房间里一共有3位体检医师,所以一次可以进入3个人,有人出来就有人可以进去
*/
public class Appliction {
public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 100; i++) {
new Thread(new PhysicalExaminationTask(semaphore)).start();
}
}
}

class PhysicalExaminationTask implements Runnable{
private Semaphore semaphore;

public PhysicalExaminationTask(Semaphore semaphore){
this.semaphore = semaphore;
}

@Override
public void run() {
try {
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
int time = new Random().nextInt(5);
if (time > 0) {
try {
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.err.println(Thread.currentThread().getName() + " finished");
semaphore.release();
}
}
### Java 中多线程编程信号量的实现方式 #### 什么是信号量信号量Semaphore)是一种用于控制多个线程对共享资源访问数量的同步工具。它可以限制同时访问某一特定资源的操作数,从而防止竞争条件的发生。Java 提供了 `java.util.concurrent.Semaphore` 类来支持信号量的功能。 #### Semaphore 的基本概念 `Semaphore` 可以维护一组许可证的概念。每当调用 `acquire()` 方法时,如果还有可用的许可,则会获取一个;如果没有剩余许可,则当前线程会被阻塞直到有新的许可释放出来。当某个线程完成操作后,可以通过调用 `release()` 方法返回一个许可给其他等待中的线程使用[^3]。 #### 创建和初始化 Signal 创建一个 `Semaphore` 对象通常需要指定初始的许可数目以及是否采用公平策略: - **参数一**:表示有多少个许可可供分配; - **参数二**(可选布尔):决定该 semaphore 是否遵循 FIFO 公平调度原则,默认为 false 表示非公平模式。 下面展示如何声明具有固定容量的 semaphores 并设置其属性: ```java // 非公平锁, 初始计数为3 Semaphore semaphore = new Semaphore(3); ``` 或者启用公平锁定机制: ```java // 使用公平算法, 同样设定最大并发连接数为3 Semaphore semaphoreFair = new Semaphore(3, true); ``` #### 应用场景举例——数据库连接池模拟 假设我们有一个简单的数据库连接池管理器,只允许最多三个客户端同时建立连接查询数据。此时就可以借助 semaphore 来协调这些请求。 ```java import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.concurrent.Semaphore; public class DBConnectionPool { private final int MAX_CONNECTIONS = 3; // 定义最大的连接数 private Connection[] connections; private Semaphore availableConnections; public DBConnectionPool(String url) throws SQLException { this.availableConnections = new Semaphore(MAX_CONNECTIONS); connections = new Connection[MAX_CONNECTIONS]; for (int i=0;i<MAX_CONNECTIONS;i++) { connections[i]=DriverManager.getConnection(url); } } /** * 获取数据库连接的方法. */ public Connection getConnection() throws InterruptedException{ try { availableConnections.acquire(); // 尝试获得一个许可 return getFreeConnection(); } catch (InterruptedException e){ throw e; } } private synchronized Connection getFreeConnection(){ for(Connection conn :connections ){ if(!conn.isClosed()){ return conn ; } } return null ; //理论上不会到达这里 } /** * 归还数据库连接到池子里面去. */ public void releaseConnection(Connection connection){ if(connection !=null && !connection.isClosed()) { try { connection.close(); // 关闭实际物理链接 }catch(SQLException ex){} availableConnections.release(); // 返回一个许可回semaphore } } } ``` 上述例子展示了如何利用 semaphore 控制同一时间内的活动线程数量不超过预设上限,并且保证每次只有一个线程能够成功取得空闲状态下的真实 JDBC 连接对象[^4]。 #### 总结 通过以上分析可以看出,在 Java 编程环境中运用 signal 不仅能有效解决多线程间资源共享问题,而且还能简化程序逻辑结构提高开发效率。然而需要注意的是,不当配置可能会引发死锁现象或者其他性能瓶颈等问题,因此在实际项目应用过程中应当谨慎考虑各种边界情况并做好充分测试工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值