本视频是解读性视频,所以希望您已经看过了本知识点的内容,并且编写了相应的代码之后,带着疑问来观看,这样收获才多。 不建议一开始就观看视频
10分40秒 本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器。 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)。 chrome 的 视频下载插件会影响播放,如 IDM 等,请关闭或者切换其他浏览器 步骤 1 : 数据库连接池原理-传统方式 步骤 2 : 数据库连接池原理-使用池 步骤 3 : ConnectionPool构造方法和初始化 步骤 4 : 测试类 步骤 5 : 练习-数据库连接池 步骤 6 : 答案-数据库连接池
当有多个线程,每个线程都需要连接数据库执行SQL语句的话,那么每个线程都会创建一个连接,并且在使用完毕后,关闭连接。
创建连接和关闭连接的过程也是比较消耗时间的,当多线程并发的时候,系统就会变得很卡顿。 同时,一个数据库同时支持的连接总数也是有限的,如果多线程并发量很大,那么数据库连接的总数就会被消耗光,后续线程发起的数据库连接就会失败。
与传统方式不同,连接池在使用之前,就会创建好一定数量的连接。
如果有任何线程需要使用连接,那么就从连接池里面借用,而不是自己重新创建. 使用完毕后,又把这个连接归还给连接池供下一次或者其他线程使用。 倘若发生多线程并发情况,连接池里的连接被借用光了,那么其他线程就会临时等待,直到有连接被归还回来,再继续使用。 整个过程,这些连接都不会被关闭,而是不断的被循环使用,从而节约了启动和关闭连接的时间。
1. ConnectionPool() 构造方法约定了这个连接池一共有多少连接
2. 在init() 初始化方法中,创建了size条连接。 注意,这里不能使用try-with-resource这种自动关闭连接的方式,因为连接恰恰需要保持不关闭状态,供后续循环使用 3. getConnection, 判断是否为空,如果是空的就wait等待,否则就借用一条连接出去 4. returnConnection, 在使用完毕后,归还这个连接到连接池,并且在归还完毕后,调用notifyAll,通知那些等待的线程,有新的连接可以借用了。 注:连接池设计用到了多线程的wait和notifyAll,这些内容可以在多线程交互章节查阅学习。 package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class ConnectionPool {
List<Connection> cs = new ArrayList<Connection>();
int size;
public ConnectionPool(int size) {
this.size = size;
init();
}
public void init() {
//这里恰恰不能使用try-with-resource的方式,因为这些连接都需要是"活"的,不要被自动关闭了
try {
Class.forName("com.mysql.jdbc.Driver");
for (int i = 0; i < size; i++) {
Connection c = DriverManager
.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8", "root", "admin");
cs.add(c);
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public synchronized Connection getConnection() {
while (cs.isEmpty()) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Connection c = cs.remove(0);
return c;
}
public synchronized void returnConnection(Connection c) {
cs.add(c);
this.notifyAll();
}
}
首先初始化一个有3条连接的数据库连接池
然后创建100个线程,每个线程都会从连接池中借用连接,并且在借用之后,归还连接。 拿到连接之后,执行一个耗时1秒的SQL语句。 运行程序,就可以观察到如图所示的效果 package jdbc;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import jdbc.ConnectionPool;
public class TestConnectionPool {
public static void main(String[] args) {
ConnectionPool cp = new ConnectionPool(3);
for (int i = 0; i < 100; i++) {
new WorkingThread("working thread" + i, cp).start();
}
}
}
class WorkingThread extends Thread {
private ConnectionPool cp;
public WorkingThread(String name, ConnectionPool cp) {
super(name);
this.cp = cp;
}
public void run() {
Connection c = cp.getConnection();
System.out.println(this.getName()+ ":\t 获取了一根连接,并开始工作" );
try (Statement st = c.createStatement()){
//模拟时耗1秒的数据库SQL语句
Thread.sleep(1000);
st.execute("select * from hero");
} catch (SQLException | InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
cp.returnConnection(c);
}
}
向数据库中插入100条数据,比较传统方式和数据库连接池方式的性能差异
1. 使用传统方式创建100个线程,每个线程都会创建新的连接,通过这个连接向数据库插入1条数据,然后关闭这个连接。 2. 使用数据库连接池的方式,创建一个有10条连接的连接池,然后创建100个线程,每个线程都会向连接池借用连接,借用到后,向数据库插入1条数据,然后归还这个连接。 通过时间统计,比较这两种方式的性能表现差异。
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
本视频是解读性视频,所以希望您已经看过了本答案的内容,带着疑问来观看,这样收获才多。 不建议一开始就观看视频
3分37秒 本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器。 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)。 chrome 的 视频下载插件会影响播放,如 IDM 等,请关闭或者切换其他浏览器
TraditionalWorkingThread 使用传统方式,建立连接的工作线程
ConnectionpoolWorkingThread 使用连接池方式,建立连接的工作线程 TestConnectionPool 测试类,在这个类中分别创建了100个TraditionalWorkingThread 和100ConnectionpoolWorkingThread ,并分别统计他们运行需要的时间
package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
class TraditionalWorkingThread extends Thread {
public void run() {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try (Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8",
"root", "admin"); Statement st = c.createStatement()) {
for (int i = 0; i < TestConnectionPool.insertTime; i++) {
String sql = "insert into hero values(null," + "'提莫'" + "," + 313.0f + "," + 50 + ")";
st.execute(sql);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package jdbc;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
class ConnectionpoolWorkingThread extends Thread {
private ConnectionPool cp;
public ConnectionpoolWorkingThread(ConnectionPool cp) {
this.cp = cp;
}
public void run() {
Connection c = cp.getConnection();
try (Statement st = c.createStatement()) {
for (int i = 0; i < TestConnectionPool.insertTime; i++) {
String sql = "insert into hero values(null," + "'提莫'" + "," + 313.0f + "," + 50 + ")";
st.execute(sql);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
cp.returnConnection(c);
}
}
package jdbc;
import java.util.ArrayList;
import java.util.List;
public class TestConnectionPool {
private static int threadNumber = 100;
public static int insertTime = 1;
public static void main(String[] args) {
traditionalWay();
connectionPoolWay();
}
private static void connectionPoolWay() {
ConnectionPool cp = new ConnectionPool(10);
System.out.println("开始连接池方式插入数据测试:");
long start = System.currentTimeMillis();
List<Thread> ts = new ArrayList<>();
for (int i = 0; i < threadNumber; i++) {
Thread t =new ConnectionpoolWorkingThread(cp);
t.start();
ts.add(t);
}
//等待所有线程结束
for (Thread t : ts) {
try {
t.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.printf("使用连接池方式,启动%d条线程,每个线程插入%d条数据,一共耗时%d 毫秒%n",threadNumber,insertTime,end-start);
}
private static void traditionalWay() {
System.out.println("开始传统方式插入数据测试:");
long start = System.currentTimeMillis();
List<Thread> ts = new ArrayList<>();
for (int i = 0; i < threadNumber; i++) {
Thread t =new TraditionalWorkingThread();
t.start();
ts.add(t);
}
//等待所有线程结束
for (Thread t : ts) {
try {
t.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.printf("使用传统方式,启动%d条线程,每个线程插入%d条数据,一共耗时%d 毫秒%n",threadNumber,insertTime,end-start);
}
}
HOW2J公众号,关注后实时获知最新的教程和优惠活动,谢谢。
问答区域
2024-07-16
比较连接池和普通方式访问数据库的性能
回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
2022-12-07
答案:数据库连接池
回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
2022-05-02
答案
2022-03-08
自己的代码,可以运行。那些连接池比传统时间长的,可能是这个原因,没有加这个代码
2021-09-06
练习 - 数据库连接池
提问太多,页面渲染太慢,为了加快渲染速度,本页最多只显示几条提问。还有 35 条以前的提问,请 点击查看
提问之前请登陆
提问已经提交成功,正在审核。 请于 我的提问 处查看提问记录,谢谢
|