前言:
调用一个方法等待一段时间,如果在给定时间内得到结果,则立刻返回;否则返回默认值。这种模式就是等待超时模式。
等待超时模式的实现:
需要对等待/通知模式做出改动
java并发编程笔记:等待/通知机制_暮色_年华的博客
假设超时时间是T,那么可以推断出在当前时间now+T之后就会超时。
定义如下变量:
等待持续时间:REMAINGING = T
超时时间:FUTURE = now + T
伪代码:
当已经获得结果或者剩余时间小于等于0,那么直接返回结果。
等待超时模式在等待/通知基础上增加了超时控制,使得该模式比原有范式更具有灵活性。
因为即使方法执行时间过长,也不会“永久”阻塞调用者,而是会按照调用者的要求按时返回。
一个简单的数据库连接池示例:
public class ConnectionPool {
/*
Connect数据库连接对象
pool:数据库连接池,用双端链表实现,里面放Connection对象
*/
private LinkedList<Connection>pool=new LinkedList<Connection>();
/*
初始化数据库连接池,即在数据库连接池中添加给定个Connection对象
* */
public ConnectionPool(int initialSize){
if(initialSize>0){
for(int i=0;i<initialSize;i++){
pool.addLast(ConnectionDriver.createConnection());
}
}
}
/*
* 释放连接
* ?为什么释放连接要addLast到pool中?
* */
public void releaseConnection(Connection connection){
if(connection!=null){
//pool是线程共享的,访问pool的时候要加锁
//为什么要对pool进行加锁,用其他对象作为锁可以吗?
synchronized (pool){
pool.addLast(connection);
//连接释放后需要进行通知,这样其他消费者能够感知连接池中已经归还了一个连接
pool.notifyAll();
}
}
}
//获取一个连接对象,如果在mills秒内无法获取到连接,将会返回null
public Connection fetchConnection(long mills) throws InterruptedException {
synchronized (pool){
if(mills<=0) {
while (pool.isEmpty()) {
pool.wait();
}
return pool.removeFirst();
}else{
long future=System.currentTimeMillis()+mills;
long remaining=mills;
//使用等待超时模式
/*
* while(获取不到结果&&remaining>0){
* 锁对象.wait(remaining);
* remaining=future-当前时间;
* }
* result=null;
* if(获取到对象)result=get();
* return result;
* */
while(pool.isEmpty()&&remaining>0){
/*
* 执行完pool.wait()之后怎么办?
* */
pool.wait(remaining);
remaining=future-System.currentTimeMillis();
}
Connection result=null;
if(!pool.isEmpty()){
result=pool.removeFirst();
}
return result;
}
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
/*
* 动态代理:
* (1)InvocationHandler接口,重写invoke方法
* (2)Proxy.newProxyInstance();创建代理类对象
* */
static class ConnectionHandler implements InvocationHandler{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("commit")){
TimeUnit.MILLISECONDS.sleep(100);
}
return null;
}
}
public static final Connection createConnection(){
return (Connection) Proxy.newProxyInstance(ConnectionDriver.class.getClassLoader(),new Class<?>[]{Connection.class},new ConnectionHandler());
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
public class ConnectionPoolTest {
//数据库连接池
static ConnectionPool pool=new ConnectionPool(10);
//保证所有ConnectionRunner线程能够同时开始运行
static CountDownLatch start=new CountDownLatch(1);
//end计数器为线程个数
static CountDownLatch end;
public static void main(String[] args) throws InterruptedException {
int threadCount=20;
//end计数器为线程数量
end=new CountDownLatch(threadCount);
int count=20;
//got和notGot是线程共享的变量,所有线程都可以对其进行修改。
//但是是线程安全的整数
AtomicInteger got=new AtomicInteger();
AtomicInteger notGot=new AtomicInteger();
for(int i=0;i<threadCount;i++){
Thread thread=new Thread(new ConnectionRunner(count,got,notGot),"ConnectionRunnerThread");
thread.start();
}
//所有线程同时开始运行
start.countDown();
//知道所有线程运行完后才能继续运行主线程
end.await();
System.out.println("total invoke:"+(threadCount*count));
System.out.println("got connection"+got);
System.out.println("not got connection"+notGot);
}
static class ConnectionRunner implements Runnable{
int count;
AtomicInteger got;
AtomicInteger notGot;
public ConnectionRunner(int count, AtomicInteger got, AtomicInteger notGot) {
this.count = count;
this.got = got;
this.notGot = notGot;
}
@Override
public void run() {
try {
//start.await()的作用是在main线程中创建threadCount个线程,它们运行start.await()后
// 会马上进入等待状态
/*
* 在创建完所有线程后start.countDown()使start的计数器变为0
* 这时所有线程同时开始运行。
* */
start.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//一共尝试获取count次Connection对象
while(count>0){
try {
//从线程池中获取连接,如果1000ms内无法获取到,返回null
//分别统计连接获取数量got和未获取到的数量notGot
Connection connection = pool.fetchConnection(1000);
if (connection != null) {
try {
//模拟事务
connection.createStatement();
connection.commit();
} finally {
//释放connection对象,把connection对象放入连接池
pool.releaseConnection(connection);
//got自增1
got.incrementAndGet();
}
} else {
//如果获取不到notGot自增1
notGot.incrementAndGet();
}
}catch (Exception exception){
}
finally {
//次数减1
count--;
}
}
//执行完一个线程end减1,因为end等于线程数,所以当线程全部执行完后end计数器变为变为0
end.countDown();;
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
统计规律:
在资源一定的情况下,随着客户端线程的逐步增加,客户端出现超时无法获取连接的比率不断升高。
虽然客户端线程在超时获取的模式下会出现连接无法获取的情况,但是能够保证客户端线程不会一直挂载连接获取的操作上,而是按时返回。
数据库连接池的设计也可以复用到其他的资源获取的场景,针对昂贵资源的获取都应该加以超时限制。