什么是Semaphore
Semaphore(信号量)是java.util.concurrent包下一个重要的工具类。Semaphore类似synchronized的加强版,作用是控制线程的并发数量。而就这一点而言,synchronized关键字是实现不了的。
Semaphore内部也是基于AQS实现的,将AQS下的state用作保存允许的流量数,访问特定资源前,必须使用acquire方法获得许可,如果许可数量为0,该线程则一直阻塞,直到有可用许可。这一点与共享锁的实现类似。
Semaphore和ReentrantLock类似,获取许可有公平策略和非公平许可策略,默认情况下使用非公平策略。当初始值为1时,可以用作互斥锁,并具备不可重入的加锁语义。
Semaphore常用的一些接口
- Semaphore(permits):构造方法,permits 为初始化许可证数量的构造函数,默认为非公平锁
- Semaphore(permits,fair)构造方法,初始化许可证数量和是否公平模式的构造函数
- acquire():当前线程尝试去阻塞的获取1个许可证,如果获取不到则阻塞当前线程
- tryAcquire():线程尝试去获取1个许可证,如果获取不到返回false继续执行,不会阻塞当前线程
- tryAcquire(timeout,TimeUnit):线程尝试去获取1个许可证,如果获取不到则阻塞等待一个超时时间,超时后返回false继续执行
- release():当前线程释放1个可用的许可证
- availablePermits():当前可用的许可证数量
- hasQueuedThreads():判断当前Semaphore对象上是否存在正在等待许可证的线程
- getQueueLength():获取当前Semaphore对象上是正在等待许可证的线程数量
模拟实现数据库连接池
通过Semaphore模拟实现一个数据库的连接池
public class DBPoolSemaphore {
private final static int POOL_SIZE = 10;
private final Semaphore useful,useless;//useful表示可用的数据库连接,useless表示已用的数据库连接
public DBPoolSemaphore() {
this.useful = new Semaphore(POOL_SIZE);
this.useless = new Semaphore(0);
}
//存放数据库连接的容器
private static LinkedList pool = new LinkedList();
//初始化池
static {
for (int i = 0; i < POOL_SIZE; i++) {
pool.addLast(SqlConnectImpl.fetchConnection());
}
}
/*归还连接*/
public void returnConnect(Connection connection) throws InterruptedException {
if(connection!=null) {
System.out.println("当前有"+useful.getQueueLength()+"个线程等待数据库连接!!"
+"可用连接数:"+useful.availablePermits());
useless.acquire();
synchronized (pool) {
pool.addLast(connection);
}
useful.release();
}
}
/*从池子拿连接*/
public Connection takeConnect() throws InterruptedException {
useful.acquire(); // 申请信号量(useful - 1),如果取不到则进入等待队列,阻塞线程
Connection conn;
synchronized (pool) {
conn = pool.removeFirst();
}
useless.release(); // 释放信号量(useless + 1)
return conn;
}
}