本文是Java线程安全和并发编程知识总结的一部分。
2.4 信号量(对象Samaphore
)
信号量是用来控制并发执行某个操作的线程数量的同步辅助工具。锁确保每个资源或同步块只能被一个线程访问(读写锁允许多个线程读,一个线程写);而信号量允许控制最多允许多少个线程并发访问。
信号量需要在构建时指定访问许可的数目。二元信号量(只有1个访问许可的信号量)相当于一个不可重入锁;因为只要有一个操作申请到了信号量,那么下个操作申请时必然失败。
信号量类提供如下方法:
- acquire: 申请一个访问许可的一系列重载方法和类似方法,如果没有许可,则阻塞当前线程直到有许可为止。
- tryAcquire: 申请一个访问许可的一系列重载方法;如果没有许可,则返回false,否则返回true。
- release: 释放已申请的访问许可的一系列重载方法。
下面通过使用信号量还改写 条件锁 一章中的连接池例子来说明如何使用信号量:
/**
* @author xx
* 2020年2月6日 上午10:44:00 xx添加此方法
*/
public class ConnectionPool<T> {
/**
* 控制连接池大小的信号量
*/
private final Semaphore sem;
/**
* 获取连接的超时时长
*/
private int fetchTimeOut;
/**
* 空闲连接的容器
*/
private LinkedList<T> freeConnections;
/**
* 正在使用的连接的容器
*/
private LinkedList<T> busyConnections;
/**
*
* 构造函数
* @param capacity 连接池大小
* @param fetchTimeOut 连接池获取连接超时时长(s)
*/
public ConnectionPool(int capacity, int fetchTimeOut) {
this.sem = new Semaphore(capacity);
this.fetchTimeOut = fetchTimeOut;
this.freeConnections = new LinkedList<T>();
this.busyConnections = new LinkedList<T>();
}
/**
* 从连接池获取一个连接
* 2020年2月6日 上午10:44:00 xx添加此方法
*/
public T fetchConnectioin(int index) {
// 如果没有空闲连接,则当前线程挂起等待有空闲连接或超时
try {
this.sem.tryAcquire(this.fetchTimeOut, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException("等待空闲连接时线程中断异常", e);
}
// 独立中监控空闲连接数并决定是否需要新增缓存的连接。
T element = this.freeConnections.remove(this.freeConnections.size() - 1);
this.busyConnections.add(element);
return element;
}
/**
* 将连接释放会连接池
* 2020年2月6日 上午10:44:00 xx添加此方法
*/
public void releaseConnection(T connection) {
if (this.busyConnections.remove(connection)) {
this.freeConnections.add(connection);
this.sem.release();
}
}
}
信号量使用时,需注意如下问题:
- 信号量构建完毕后,是无法修改许可数的。
- 信号量并不和线程绑定。它只关注颁发了多少许可,是否有释放许可,但并不关心释放许可的是否当初申请需求的那个线程。
网友评论