Java 数据连接池的实现

本文介绍了一种使用Java和Mysql实现的简单数据库连接池解决方案,通过优化Mysql配置提升并发性能,并探讨了连接池的工作原理及其实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Mysql版本5.6

java 在进行数据库操作时每次需要进行物理连接一次数据库,物理连接一次平均是150mm(数值只针对我自己电脑)。

 缺点:开销太大

在写连接池的时候来了解一下mysql配置 ,WIN找 my.ini (默认位置:C:\ProgramData\MySQL\版本)   

Linux找 my.cnf  执行 sudo find / -name my.cnf 进行查找

mysql默认并发数是151 我们可以设置为  max_connections=1000

max_connections 必须设置否在下面代码连接过151时会出错

innodb处理io读写的后台并发线程数量,根据cpu核来确认
默认值:4,建议值:与逻辑cpu数量的一半保持一致。
innodb_read_io_threads = 4
innodb_write_io_threads = 4
MySQL默认的wait_timeout  值为8个小时, interactive_timeout参数需要同时配置才能生效
MySQL连接闲置超过一定时间后(单位:秒,此处为1800秒)将会被强行关闭

为了更好的测试我设置为 120秒
interactive_timeout = 120 
wait_timeout = 120

下面代码只是用队列简单实现了连接池

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * @author XiaoTian
 */
public class BlockingQueueConnection implements Runnable {
	
	//单例对象
	private static BlockingQueueConnection blocking = new BlockingQueueConnection();

	private BlockingQueue<Connection> blockingQueue;
	
	private DataSource dataSource;
	
	//获取对象
	public static BlockingQueueConnection getBlock() {
		return blocking;
	}
	
	public BlockingQueueConnection setBlockQueue(DataSource dataSource) {
		if(null == blockingQueue || blockingQueue.size() <= 0) {
			blockingQueue = new LinkedBlockingQueue<Connection>();
			this.dataSource = dataSource;
		}
		return blocking;
	}

	public void run() {
		// TODO Auto-generated method stub
		try {
			//1.加载驱动
	    	Class.forName(dataSource.getJdbcDriverName());
			while(blocking.blockingQueue.size() < dataSource.getInitialSize()) {
		    	//获取连接
		    	Connection connection =  DriverManager.getConnection(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
		    	blockingQueue.put(connection);
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static Connection getConnection() {
		return blocking.blockingQueue.poll();
	}
	
	public static void setConnection(Connection connection) throws InterruptedException {
		blocking.blockingQueue.put(connection);
	}
	
	public static BlockingQueue<Connection> get(){
		return blocking.blockingQueue;
	}

}

使用 LinkedBlockingQueue put方法和take方法都进行加锁操作。获取take的时候,检索并移除此队列的头,在必要时等待,直到一个元素可用。put 在这个队列的尾部插入指定的元素,在必要时等待空间可用。在使用take和put方法的时候没有空间使用就和进行堵塞直到可用的空间

从数据结构放面看 LinkedBlockingQueue (先进先出)put从尾部插入是O1操作,take获取头部 O1操作。效率当然快

启动线程进行测试

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class Test {

	private final static String username = "root";
	
	private final static String password = "111111";
	
	private final static String url = "jdbc:mysql:///test?useSSL=true&useUnicode=true&characterEncoding=utf8";
	
	private final static String jdbcDriverName = "com.mysql.jdbc.Driver";
	
	public static void main(String[] args) throws InterruptedException, SQLException {
		DataSource dataSource = new DataSource(url, username, password, jdbcDriverName);
		BlockingQueueConnection blockingQueue = BlockingQueueConnection.getBlock();
		long time = System.currentTimeMillis();
		blockingQueue.setBlockQueue(dataSource);
		blockingQueue.run();
		System.out.println((System.currentTimeMillis() - time)/1000d);
		System.out.println(BlockingQueueConnection.get().size());
		
		Connection connection1 = BlockingQueueConnection.getConnection();
		Connection connection2 = BlockingQueueConnection.getConnection();
		System.out.println(connection1 == connection2);
		new Thread(new Runnable() {	
			@Override
			public void run() {
				// TODO Auto-generated method stub
				int i = 0;
				while(i++ < 1000) {
					ResultSet rs = null;
					Statement st = null;
					try {
						Connection connection =  BlockingQueueConnection.getConnection();
						st = connection.createStatement();
						String sql = "SELECT id FROM `user` where id =4";	
					       rs = st.executeQuery(sql);
					        //5.取出结果集的数据
				        	while(rs.next()){ 
				        		System.out.println(rs.getObject("id")+ " "+Thread.currentThread().getName()+" :" +  " S1>>"+i);
				        	}
				        BlockingQueueConnection.setConnection(connection);
					} catch (SQLException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}finally {
						//6.关闭链接,释放资源
						try {
							rs.close();
							st.close();
						} catch (SQLException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} 
					}
			   }
			   
			}
		}).start();
		new Thread(new Runnable() {	
			@Override
			public void run() {
				// TODO Auto-generated method stub
				int i = 0;
					while(i++ < 5000) {
						ResultSet rs = null;
						Statement st = null;
						try {
							Connection connection =  BlockingQueueConnection.getConnection();
							st = connection.createStatement();
							String sql = "SELECT id FROM `user` where id =4";	
						       rs = st.executeQuery(sql);
						        //5.取出结果集的数据
					        	while(rs.next()){ 
					        		System.out.println(rs.getObject("id")+ " "+Thread.currentThread().getName()+" :" +  " S2>>"+i);
					        	}
					        BlockingQueueConnection.setConnection(connection);
						} catch (SQLException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}finally {
							//6.关闭链接,释放资源
							try {
								rs.close();
								st.close();
							} catch (SQLException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							} 
						}
				}
			}
		}).start();
	}
}

这样一个简单连接池就实现了,每次从加载里获取 Connection 连接 用完再放回去

ResultSet和Statement 用完之后 再finally 里进行关闭,如果不关闭内存内增加并且导致内存溢出。


public class DataSource {
	  
	private volatile String url;
	
	private volatile String username;
	
	private volatile String password;
	
	private volatile String jdbcDriverName;
	
	/**
	 * 初始化大小
	 */
	private volatile Integer initialSize= 500; 
	
	public DataSource() {
		this(null,null,null,null);
	}
	
	/**
	 * 
	 * @param url	
	 * @param username	用户名
	 * @param password  密码
	 * @param jdbcDriverName  启动全类名
	 */
	public DataSource(String url, String username, String password, String jdbcDriverName) {
		super();
		this.url = url;
		this.username = username;
		this.password = password;
		this.jdbcDriverName = jdbcDriverName;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getJdbcDriverName() {
		return jdbcDriverName;
	}

	public void setJdbcDriverName(String jdbcDriverName) {
		this.jdbcDriverName = jdbcDriverName;
	}

	public Integer getInitialSize() {
		return initialSize;
	}

	public void setInitialSize(Integer initialSize) {
		this.initialSize = initialSize;
	}
	
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值