我启动了十个线程去测试一个业务方法,但有时候会报出连接已经关闭,无法继续操作的异常。直接上代码了:
首先是测试代码:
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();
}
}
}
其中一次得到的运行结果:
Masalahnya terletak pada baris kod ini dalam DBUtil:
Pembolehubah sambung ialah pembolehubah statik global dan dikongsi oleh semua urutan.
Kes yang melampau:
Apabila thread A dijalankan ke conn = DriverManager.getConnection(...), conn ialah pangkalan data sambungan;
Thread B laksanakan ke conn = DriverManager.getConnection(...) Apabila conn ditugaskan semula sebagai sambungan pangkalan data
Pada masa ini, memandangkan conn dikongsi oleh semua thread, nilai conn untuk thread A dan thread B ialah sambungan pangkalan data b. Ini menyebabkan semua utas berkongsi sambungan pangkalan data b Apabila satu utas menutup sambungan, utas lain akan membuang pengecualian.
Selain itu, kerana sambungan pangkalan data b dikongsi pada penghujungnya, pangkalan data sambungan tidak boleh ditutup, menyebabkan kebocoran memori.
Perlu dikatakan bahawa tiada jaminan bahawa sambungan yang berbeza akan dikembalikan setiap kali
Lagipun, anda menulisnya sebagai pembolehubah ahli
Untuk menggunakan Pool, mulakan berbilang dan tetapkan satu pada setiap sambungan.