探索多线程使用同一个数据库connection的后果
在项目中看到有用到数据库的连接池,心里就思考着为什么需要数据库连接池,只用一个连接会造成什么影响?(只用一个connection)?
1 猜想:jdbc的事务是基于connection的,如果多线程共用一个connection,会造成多线程之间的事务相互干扰。(connection.setAutoCommit(false);//connection.commit())
2 于是就模仿以下场景来做一个测试:
在多用户请求的情况下,只用一个数据库connection。
1)获取connection工具类:
package jdbcPool.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class ConnectorUtil {
public static final String user="root";
public static final String pwd="123456";
public static final String driver="com.mysql.jdbc.Driver";
public static final String url ="jdbc:mysql://localhost:3306/test";
private static Connection conn;
private static int connectCount=0;
static {
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
System.out.println("找不到数据库驱动..");
e.printStackTrace();
}
}
/**
* 获取数据库连接实例
* @return
*/
public synchronized static Connection getInstance(){
if(conn==null){
try {
conn=DriverManager.getConnection(url,user, pwd);
conn.setAutoCommit(false);//设置为不自动提交。。。
connectCount++;
System.out.println("连接数据库次数:"+connectCount);
} catch (SQLException e) {
System.out.println("连接数据库失败....");
e.printStackTrace();
}
}
return conn;
}
}
2) 业务接口实现类:
package jdbcPool.business;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import jdbcPool.util.ConnectorUtil;
public class StudentService {
private Connection conn;
private static StudentService studentService;
private StudentService(){
conn=ConnectorUtil.getInstance();
}
public static synchronized StudentService getInstance(){
if(studentService==null){
studentService=new StudentService();
}
return studentService;
}
public void insert(String id,String name,String no) throws Exception {
String addStr ="insert into student(id,name,no) values('"+id+"','"+name+"','"+no+"')";
Statement statement=null;
try {
statement = conn.createStatement();
statement.execute(addStr);
if("1350".equals(id)){//模仿某个线程执行service某个方法中某个步骤出现异常
Thread.sleep(3000);//模仿当前线程执行时间较长。。。。。
System.out.println("发生异常。。。。。");
System.out.println("记录"+id+"插入失败。。。。");
conn.rollback(); //出现异常事务回滚。。。
throw new Exception();
}else{
conn.commit();
System.out.println("记录"+id+"插入成功。。。。");
}
} catch (SQLException e) {
System.out.println("创建statement失败");
e.printStackTrace();
}finally{
if(statement!=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
3)模拟用户请求的线程类:
package jdbcPool.thread;
import jdbcPool.business.StudentService;
public class Request implements Runnable{
private String id;
public Request(String id) {
this.id=id;
}
@Override
public void run() {
//模仿service的单例模式
try {
StudentService.getInstance().insert(this.id, "name"+id, "no"+id);
} catch (Exception e) {
e.printStackTrace();
}
}
}
4) 测试类:
package jdbcPool.test;
import jdbcPool.thread.Request;
public class Main {
//两百个线程并发访问同一个connection
public static void main(String[] args){
for(int i=1300;i<1500;i++){
Thread th=new Thread(new Request(String.valueOf(i)));
th.start();
}
}
}
5)结果分析:
打印台出现的结果:
记录1489插入成功。。。。
记录1490插入成功。。。。
记录1491插入成功。。。。
记录1495插入成功。。。。
记录1492插入成功。。。。
记录1493插入成功。。。。
记录1494插入成功。。。。
记录1496插入成功。。。。
记录1497插入成功。。。。
记录1498插入成功。。。。
记录1499插入成功。。。。
记录1300插入成功。。。。
发生异常。。。。。
记录1350插入失败。。。。
java.lang.Exception
at jdbcPool.business.StudentService.insert(StudentService.java:38)
at jdbcPool.thread.Request.run(Request.java:18)
at java.lang.Thread.run(Unknown Source)
数据库中的表数据:
id为1350的记录竟然成功的添加进数据库了,造成这一现象的原因显然是
在添加id为1350的记录的线程遇到异常还没有来得及数据回滚时,
别的线程先调用了 connection.commit()方法,以至于把不该提交的数据提交到数据库了。
6) 总结:在多线程的环境中,在不对connection做线程安全处理的情况下,使用单个connection会引起事务的混乱....影响jdbc事务的使用。。。