一、前言
今天我们通过wait,notify/notifyAll的知识实现一个等待超时的数据库连接池
二、代码
1、自定义一个数据库连接类
package com.it.test.thread;
public class MyConnction {
public void createStatement() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void commit() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2、创建连接池
package com.it.test.thread;
import java.util.LinkedList;
public class ConnectionPool {
/**
* 数据库连接池
*/
LinkedList<MyConnction> pools = new LinkedList<>();
/**
* 默认1S的超时时间
*/
private static final int TIME = 100;
/**
* 初始化连接池的
*
* @param size
*/
public ConnectionPool(int size) {
for (int i = 0; i < size; i++) {
pools.addLast(new MyConnction());
}
}
/**
* 从连接池中获取连接
*
* @return
*/
public MyConnction getPool() {
synchronized (pools) {
long outTime = System.currentTimeMillis() + TIME;
/**
* 如果连接是空的,并且超时时间未结束,则等待休眠
*/
while (pools.isEmpty() && (outTime - System.currentTimeMillis()) > 0) {
try {
//每次被唤醒之后(有可能被其他线程唤醒)
//根据计算剩余的超时时间继续休眠
pools.wait(outTime - System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (!pools.isEmpty()) {
return pools.removeFirst();
} else {
return null;
}
}
}
/**
* 释放连接
*/
public void releasePool(MyConnction connection) {
if (connection != null) {
synchronized (pools) {
pools.addLast(connection);
pools.notifyAll();
}
}
}
}
3、编写测试类
package com.it.test.thread.pool;
import com.it.test.thread.ConnectionPool;
import com.it.test.thread.MyConnction;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
/**
*类说明:
*/
public class DBPoolTest {
static ConnectionPool pool = new ConnectionPool(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
MyConnction connection = pool.getPool();
if (connection != null)
{
try {
connection.createStatement();
// PreparedStatement preparedStatement
// = connection.prepareStatement("");
// preparedStatement.execute();
connection.commit();
} finally {
pool.releasePool(connection);
got.incrementAndGet();
}
} else {
notGot.incrementAndGet();
System.out.println(Thread.currentThread().getName()
+"等待超时!");
}
} catch (Exception ex) {
} finally {
count--;
}
}
end.countDown();
}
}
}
总共尝试了: 1000
拿到连接的次数: 652
没能连接的次数: 348
总结
wait,notify/notifyAll属于Object下的方法,使用的时候必须在同步代码块内,也就是必须在synchronized 关键字修饰的代码内。
- 调用wait方法的时候,会释放当前的锁,进入等待状态
- 调用notify和notifyAll方法,会随机唤醒或者全部唤醒一个线程,尽量使用notifyAll。因为notifyAll唤醒的是全部线程。当之前进入wait状态的线程收到notify唤醒通知之后,重新竞争锁,如果竞争到了,那么就在调用wait方法的地方继续往下执行。
-调用wait方法会释放当前锁,而调用notify和notifyAll不会释放当前锁,只有执行完notify部分的代码完,才会释放锁,所以通常我们把调用notify方法方在最后。
网友评论