探索多線程使用同一個數據庫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事務的使用。。。

 

   


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM