什么是超时等待?

调用一个方法时,等待一段时间(一般给定一个时间段),如果该方法能够在给定的时间段内得到结果,那么将结果立刻返回,反之,超时返回默认结果。这就是超时等待。

等待和通知的标准范式:即加锁,条件循环和处理逻辑三个步骤,而这种范式无法做到超时等待。

 

由于经典的等待/通知范式无法做到超时等待,也就是说,当消费者在获得锁后,如果条件不满足,等待生产者改变条件之前会一直处于等待状态,在一些实际应用中,会浪费资源,降低运行效率。

事实上,只要对经典范式做出非常小的改动,就可以加入超时等待。

超时模式的标准范式

假设  等待时间时长为T,当前时间now+T以后超时

 

long  overtime = now+T;

long remain = T;//等待的持续时间

while(result不满足条件&& remain>0){

wait(remain);

remain = overtime – now;//等待剩下的持续时间

}

return result;

 

例子:使用等到超时模式实现数据库连接池

 

package com.caojiulu;

import java.sql.Connection;
import java.util.LinkedList;

/**
*@author caojiulu
*
*类说明:实现一个数据库的连接池
*/
public class DBPool {

//数据库池的容器
private static LinkedList<Connection> pool = new LinkedList<>();

public DBPool(int initalSize) {
if(initalSize>0) {
for(int i=0;i<initalSize;i++) {
pool.addLast(SqlConnectImpl.fetchConnection());
}
}
}

//在mills时间内还拿不到数据库连接,返回一个null
public Connection fetchConn(long mills) throws InterruptedException {
synchronized (pool) {
if (mills<0) {
while(pool.isEmpty()) {
pool.wait();
}
return pool.removeFirst();
}else {
long overtime = System.currentTimeMillis()+mills;
long remain = mills;
while(pool.isEmpty()&&remain>0) {
pool.wait(remain);
remain = overtime - System.currentTimeMillis();
}
Connection result = null;
if(!pool.isEmpty()) {
result = pool.removeFirst();
}
return result;
}
}
}

//放回数据库连接
public void releaseConn(Connection conn) {
if(conn!=null) {
synchronized (pool) {
pool.addLast(conn);
pool.notifyAll();
}
}
}


}

测试类:

package com.caojiulu;

import java.sql.Connection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

/**
*@author caojiulu
*
*类说明:
*/
public class DBPoolTest {
static DBPool pool = new DBPool(10);
// 控制器:控制main线程将会等待所有Woker结束后才能继续执行
static CountDownLatch end;

public static void main(String[] args) throws Exception {
// 线程数量
int threadCount = 50;
end = new CountDownLatch(threadCount);
int count = 20;//每个线程的操作次数
AtomicInteger got = new AtomicInteger();//计数器:统计可以拿到连接的线程
AtomicInteger notGot = new AtomicInteger();//计数器:统计没有拿到连接的线程
for (int i = 0; i < threadCount; i++) {
Thread thread = new Thread(new Worker(count, got, notGot),
"worker_"+i);
thread.start();
}
end.await();// main线程在此处等待
System.out.println("总共尝试了: " + (threadCount * count));
System.out.println("拿到连接的次数: " + got);
System.out.println("没能连接的次数: " + notGot);
}

static class Worker implements Runnable {
int count;
AtomicInteger got;
AtomicInteger notGot;

public Worker(int count, AtomicInteger got,
AtomicInteger notGot) {
this.count = count;
this.got = got;
this.notGot = notGot;
}

public void run() {
while (count > 0) {
try {
// 从线程池中获取连接,如果1000ms内无法获取到,将会返回null
// 分别统计连接获取的数量got和未获取到的数量notGot
Connection connection = pool.fetchConn(1000);
if (connection != null) {
try {
connection.createStatement();
connection.commit();
} finally {
pool.releaseConn(connection);
got.incrementAndGet();
}
} else {
notGot.incrementAndGet();
System.out.println(Thread.currentThread().getName()
+"等待超时!");
}
} catch (Exception ex) {
} finally {
count--;
}
}
end.countDown();
}
}
}