我启动了十个线程去测试一个业务方法,但有时候会报出连接已经关闭,无法继续操作的异常。直接上代码了:
首先是测试代码:
public class Test {
public static void main(String[] args){
// ProductService productService = new ProductServiceImpl();
// productService.updateProductPrice(1, 3000);
for(int i=0;i<10;i++){
ProductService productService = new ProductServiceImpl();
MyThread myThread = new MyThread(productService);
myThread.start();
}
}
}
class MyThread extends Thread{
private ProductService productService;
public MyThread(ProductService productService){
this.productService = productService;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
productService.updateProductPrice(1,3000);
}
}
业务代码:
public class ProductServiceImpl implements ProductService{
private static final String UPDATE_PRODUCT_SQL = "update product set price = ? where id = ?";
private static final String INSERT_LOG_SQL = "insert into log (created, description) values (?, ?)";
public void updateProductPrice(long productId, int price) {
Connection conn = null;
try {
// 获取连接
conn = DBUtil.getConnection();
System.out.println(Thread.currentThread().getName()+"---->"+conn.toString());
conn.setAutoCommit(false); // 关闭自动提交事务(开启事务)
// 执行操作
updateProduct(conn, UPDATE_PRODUCT_SQL, productId, price); // 更新产品
insertLog(conn, INSERT_LOG_SQL, "Create product."); // 插入日志
// 提交事务
conn.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭连接
DBUtil.closeConnection(conn);
}
}
private void updateProduct(Connection conn, String updateProductSQL, long productId, int productPrice) throws Exception {
PreparedStatement pstmt = conn.prepareStatement(updateProductSQL);
pstmt.setInt(1, productPrice);
pstmt.setLong(2, productId);
int rows = pstmt.executeUpdate();
if (rows != 0) {
System.out.println("Update product success!");
}
}
private void insertLog(Connection conn, String insertLogSQL, String logDescription) throws Exception {
PreparedStatement pstmt = conn.prepareStatement(insertLogSQL);
pstmt.setString(1, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()));
pstmt.setString(2, logDescription);
int rows = pstmt.executeUpdate();
if (rows != 0) {
System.out.println("Insert log success!");
}
}
}
数据库操作类:
public class DBUtil {
private static final String driver = "com.mysql.jdbc.Driver";
private static final String url = "jdbc:mysql://localhost:3306/demo";
private static final String username = "root";
private static final String password = "root";
// 定义一个数据库连接
private static Connection conn = null;
// 获取连接
public static Connection getConnection() {
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, username, password);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
// 关闭连接
public static void closeConnection(Connection conn) {
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
其中一次得到的运行结果:
問題は、DBUtil の次のコード行にあります。 リーリー
conn 変数はグローバル静的変数であり、すべてのスレッドで共有されます。スレッド A が conn = DriverManager.getConnection(...) を実行すると、conn はデータベース接続になります。
この時、connは全スレッドで共有されるため、スレッドAとスレッドBのconnの値がデータベースbの接続となります。これにより、すべてのスレッドがデータベース接続を共有することになり、1 つのスレッドが接続を閉じると、他のスレッドが例外をスローします。スレッド B が conn = DriverManager.getConnection(...) を実行すると、conn はデータベースに再割り当てされます。 b;
毎回異なる接続が返されるという保証はありません
結局のところ、それをメンバー変数として記述したのです
プールを使用するには、複数のプールを初期化し、各接続に 1 つを割り当てます。
リーリー