Java 使用动态代理和观察者模式+threadlocal实现数据库连接池

当我们使用数据库连接池时,如果多线程连接数据库时,当超过5个线程连接数据库时,那么在第6个线程包括之后就连接不到数据库了,该怎么处理。
这里用了java的动态代理来代理数据库的连接,拦截连接的close方法。并且给代理的连接加上一个时间属性,和实时监控的线程。初始化5个连接放到栈里当做数据库连接池。当连接执行数据库操作时,则去调用真正的连接方法,且当连接用完时,将连接的时间清零,放回连接池里。如果大于5个连接数据库时,连接池没有连接时,则创建新的连接放进连接池里,当连接关闭时,如果连接池里已有5个连接,多余的连接如果超过10秒没被使用,则关闭连接。
由于因为连接池的数量发生变化时,要去重新创建新的连接,所以这里使用了观察者模式,创建观察者和被观察者。当连接池的数量为空时,则就通知观察者去重新创建新的连接。
当然为了在多线程环境下,防止自己的连接被其它线程篡改,导致线程不安全,这里使用了ThreadLocal。
threadlocal是一个数据结构,有点像HashMap,可以保存"key : value"键值对,但是一个ThreadLocal只能保存一个,并且各个线程的数据互不干扰。使用threadlocal将连接作为对象放到threadloacal里,实现只有该线程自己可以访问这个连接。
代码如下:

package pool;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Observable;
import java.util.Observer;
import java.util.Stack;

//继承Observable接口来实现观察者模式
public class PoolServer extends Observable {
	// 创建数据库连接池
	private Stack<ConnProxy> pool = new Stack<ConnProxy>();
	/*
	 * threadlocal是一个数据结构,有点像HashMap,可以保存"key : value"键值对,
	 * 但是一个ThreadLocal只能保存一个,并且各个线程的数据互不干扰。
	 * 这里使用threadlocal将连接作为对象放到threadloacal里,实现只有该线程自己可以访问这个连接。
	 */
	private ThreadLocal<ConnProxy> threadLocal = new ThreadLocal<ConnProxy>();

	// 在构造器初始化五个连接
	public PoolServer() {
		// 设置观察者实现Observer接口的唯一方法update
		this.addObserver(new Observer() {
			public void update(Observable o, Object arg) {
				try {
					for (int i = 0; i < 5; i++) {
						Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/dvdstore", "root",
								"root");
						ConnProxy connProxy = new ConnProxy();
						connProxy.setConn(conn);
						pool.push(connProxy);
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
		this.setChanged();
		this.notifyObservers();
	}

//代理连接类
	public class ConnProxy {
		// 连接属性
		private Connection conn;
		private int idle;// 时间

		public void setIdle(int idle) {
			this.idle = idle;
		}

		public void setConn(Connection conn) {
			this.conn = conn;
		}

		public Connection getConn() {
			return conn;
		}

		public ConnProxy() {
			// 设置空闲时间,如果空闲时间超过10秒,则回收
			new Thread(new Runnable() {
				public void run() {
					try {
						while (true) {
							Thread.sleep(1000);
							idle += 1000;
							if (idle > 100000) {
								synchronized (Object.class) {
									if (pool.size() > 5) {
										conn.close();
										pool.remove(ConnProxy.this);
										break;
									}
								}
							}
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}).start();
		}
	}

	// 代理连接方法
	public Connection getConn() {
		return (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), new Class[] { Connection.class },
				new InvocationHandler() {
					public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
						// 判断threadlocal是否有连接对象
						if (threadLocal.get() == null) {
							Q: while (true) {
								// 监控连接池是不是空,如果不为空,则将连接放到threadlocal
								synchronized (Object.class) {
									while (!pool.isEmpty()) {
										threadLocal.set(pool.pop());
										break Q;
									}
								}
								// 设置被观察者,监控到空时,则去通知观察者创建新的连接
								while (pool.isEmpty()) {
									setChanged();
									notifyObservers();
									break;
								}
							}
						}
						// 获取代理连接
						ConnProxy p = threadLocal.get();
						if (method.getName().equals("close")) {
							p.setIdle(0);// 被使用过的idle从0开始
							pool.push(p);
							return null;// 不让其调用真正的close方法
						} else {
							Connection conn = p.getConn();
							return method.invoke(conn, args);// 调用其真正的connetion方法
						}
					}
				});
	}

	// 测试
	public static void main(String[] args) throws Exception {
		final PoolServer poolServer = new PoolServer();
		// Thread.sleep(1000);
		for (int i = 0; i < 12; i++) {
			new Thread(new Runnable() {
				public void run() {
					try {
						Connection conn = poolServer.getConn();
						conn.prepareStatement("select * from bird");
						System.out.println(conn);
						conn.close();
					} catch (Exception e) {
						e.printStackTrace();
					}

				}
			}).start();
		}
		System.in.read();
	}
}

代码分析:
一开始创建poolserver对象时会初始化五个代理连接放到连接池里,并给连接加上时间属性,检测是否为空闲连接。当连接池的数量超过5个且空闲时间超过10秒则关闭连接。
当调用getConn的方法的时候,会去代理连接对象,当第一次获取连接时,threadlocal为空,进入while循环,判断连接池是否为空,如果不为空,则将连接对象放进threadlocal,跳出循环,如果判断为空,通知观察者创建新的连接。然后获取代理连接,拦截close方法,如果不是执行close方法则去调用真正的连接方法,当执行close方法时,就将连接的时间属性设置为0,将连接放回连接池。
这就是使用动态代理和观察者模式+threadlocal实现数据库连接池连接池的实现过程。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值