-
使用 Semaphore 限流,在访问高峰期时,让请求线程阻塞,高峰期过去再释放许可,当然它只适合限制单机线程数量,并且仅是限制线程数,而不是限制资源数,资源数比如享元模式下的数据库连接池的 poolSize,或者说是线程池的核心线程数(例如连接数,请对比 Tomcat LimitLatch 的实现)
-
用 Semaphore 实现简单连接池,对比『享元模式』下的实现(用wait notify),性能和可读性显然更好,注意下面的实现中线程数和数据库连接数是相等的
-
所以,资源数和线程数一样的时候,这时候用信号量就合适了,比如数据库连接池,下面我将对之前实现的数据库连接池做一个改进
public class TestPoolSemaphore {
public static void main(String[] args) {
Pool pool = new Pool(2);
for (int i = 0; i < 5; i++) {
new Thread(() -> {
Connection conn = pool.borrow();
try {
Thread.sleep(1000); // 模拟线程执行时间
} catch (InterruptedException e) {
e.printStackTrace();
}
pool.free(conn);
}).start();
}
}
}
@Slf4j(topic = "c.ool")
class Pool {
// 1. 连接池大小
private final int poolSize;
// 2. 连接对象数组
private Connection[] connections;
// 3. 连接状态数组 0 表示空闲, 1 表示繁忙
private AtomicIntegerArray states;
private Semaphore semaphore;
// 4. 构造方法初始化
public Pool(int poolSize) {
this.poolSize = poolSize;
// 让许可数与资源数一致
this.semaphore = new Semaphore(poolSize);
this.connections = new Connection[poolSize];
this.states = new AtomicIntegerArray(new int[poolSize]);
for (int i = 0; i < poolSize; i++) {
connections[i] = new MockConnection("连接" + (i+1));
}
}
// 5. 借连接
public Connection borrow() {// t1, t2, t3
// 获取许可
try {
semaphore.acquire(); // 没有许可的线程,在此 park 等待
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < poolSize; i++) {
// 获取空闲连接
if(states.get(i) == 0) {
if (states.compareAndSet(i, 0, 1)) {
log.debug("borrow {}", connections[i]);
return connections[i];
}
}
}
// 不会执行到这里,写这个主要是保证语法不报错
return null;
}
// 6. 归还连接
public void free(Connection conn) {
for (int i = 0; i < poolSize; i++) {
if (connections[i] == conn) {
states.set(i, 0);
log.debug("free {}", conn);
semaphore.release();
break;
}
}
}
}
class MockConnection implements Connection {
private String name;
public MockConnection(String name) {
this.name = name;
}
@Override
public String toString() {
return "MockConnection{" +
"name='" + name + '\'' +
'}';
}
}
输出
23:41:12.086 c.Pool [Thread-1] - borrow MockConnection{name='连接2'}
23:41:12.086 c.Pool [Thread-0] - borrow MockConnection{name='连接1'}
23:41:13.105 c.Pool [Thread-1] - free MockConnection{name='连接2'}
23:41:13.105 c.Pool [Thread-2] - borrow MockConnection{name='连接1'}
23:41:13.105 c.Pool [Thread-0] - free MockConnection{name='连接1'}
23:41:13.105 c.Pool [Thread-3] - borrow MockConnection{name='连接2'}
23:41:14.120 c.Pool [Thread-3] - free MockConnection{name='连接2'}
23:41:14.120 c.Pool [Thread-2] - free MockConnection{name='连接1'}
23:41:14.120 c.Pool [Thread-4] - borrow MockConnection{name='连接1'}
23:41:15.120 c.Pool [Thread-4] - free MockConnection{name='连接1'}
演示完毕