[英]java dbcp2 multithreaded connection access
我非常熟悉在 tomcat 上使用連接池,並且多年來一直使用它沒有問題。 然而,目前我正在開發一個 main 方法應用程序,該應用程序出於性能原因需要同時運行線程,並且這些線程每個都需要訪問同一個數據庫。 如果我完全刪除數據庫代碼並且只使用 arrays 進行測試(例如多線程工作),我的代碼就可以工作了,但是一旦我重新添加數據庫連接,第一個線程就會鎖定而其他線程不會根本跑不起來。 玩過c3p0、dbcp2; 目前正在使用 dbcp2。 謝謝,那里有大量的文檔。 但似乎特定於我的用例的代碼示例並不多:這是一個示例應用程序:
import java.sql.*;
import org.apache.commons.dbcp2.ConnectionFactory;
import org.apache.commons.dbcp2.DriverManagerConnectionFactory;
import org.apache.commons.dbcp2.PoolableConnection;
import org.apache.commons.dbcp2.PoolableConnectionFactory;
import org.apache.commons.dbcp2.PoolingDataSource;
import org.apache.commons.dbcp2.PoolingDriver;
import org.apache.commons.dbcp2.Utils;
import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPool;
public class SandboxApp {
private static BasicDataSource dataSource;
public static BasicDataSource getDataSource() {
if (dataSource == null) {
BasicDataSource ds = new BasicDataSource();
ds.setUrl("jdbc:mysql://localhost:3306/my-db");
ds.setUsername("root");
ds.setPassword("");
ds.setDriverClassName("org.mariadb.jdbc.Driver");
ds.setInitialSize(3);
ds.setMaxTotal(25);
ds.setMinIdle(0);
ds.setMaxIdle(8);
ds.setMaxOpenPreparedStatements(100);
dataSource = ds;
}
return dataSource;
}
public static void main(String [] args) throws Exception{
for(int i=0; i<11; i++){//spawn 11 threads & get each thread to process 600k sql rows at the same time
new Thread("" + (i*600000)){
public void run(){
System.out.println("Thread: " + getName() + " running");//prints correctly for all threads
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = SandboxApp.getDataSource().getConnection();
pstmt = con.prepareStatement("select something from some_table limit "+getName()+",600000");
rs=pstmt.executeQuery();
while(rs.next()){
System.out.println("Doing stuff for thread "+getName());//this only prints for getName() == 0
//give the other threads a turn...
try {
Thread.sleep(10);
}
catch(InterruptedException ex) {
}
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {pstmt.close();} catch (SQLException e) {}
try { con.close(); } catch(Exception e) {}
try { rs.close(); } catch(Exception e) {}
}
}
}.start();
}
}
}
@user207421 是對的,getDataSource() 方法應該是同步的,當然我已經嘗試過了; 但是,這仍然沒有解決我的線程“0”不讓其他線程輪流的問題。
我從我的代碼、所有其他庫等中剝離了所有內容。直到我讓它工作,然后重新開始構建它以找到斷點。 似乎主要的決定因素是 ResultSet 的大小。 我嘗試在不同的地方添加額外的 thread.sleep 時間,但是唯一有效的方法是分解查詢以請求更小的結果集。
600k 結果集,只有 1 個線程將運行,1k ResultSets 和 4 個線程將運行。 如果 ResultSet 僅包含 100 行,則所有 11 個線程都將運行。 請注意,我是在 16 CPU 系統上測試的,其中 8GB 的 memory 分配給 JVM (aws m5.4xlarge),因此硬件資源不應該是一個促成因素。 所以我想我只需要把我的代碼分成更小的塊。
當我最初研究這個問題時,我很驚訝缺乏針對這個特定問題的特定代碼示例(無論 ResultSet 大小和線程數如何),所以我只是在這里發布最終對我有用的東西完整的代碼示例:
import java.sql.*;
import org.apache.commons.dbcp2.BasicDataSource;
public class SandboxApp {
private static BasicDataSource dataSource;
public static synchronized BasicDataSource getDataSource() {
if (dataSource == null) {
BasicDataSource ds = new BasicDataSource();
ds.setUrl("jdbc:mysql://localhost:3306/my-db");
ds.setUsername("root");
ds.setPassword("");
ds.setDriverClassName("org.mariadb.jdbc.Driver");
ds.setInitialSize(3);
ds.setMaxTotal(25);
ds.setMinIdle(0);
ds.setMaxIdle(8);
ds.setMaxOpenPreparedStatements(100);
dataSource = ds;
}
return dataSource;
}
public static void main(String [] args) throws Exception{
for(int i=0; i<11; i++){//spawn 11 threads & get each thread to process 100 sql rows at the same time
new Thread("" + (i*100)){
public void run(){
System.out.println("Thread: " + getName() + " running");
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = SandboxApp.getDataSource().getConnection();
pstmt = con.prepareStatement("select something from some_table limit "+getName()+",100");
rs=pstmt.executeQuery();
while(rs.next()){
System.out.println("Doing stuff for thread "+getName());//With smaller ResultSet, this works fine for all 11 threads
//give the other threads a turn...
try {
Thread.sleep(10);
}
catch(InterruptedException ex) {
}
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {pstmt.close();} catch (SQLException e) {}
try { con.close(); } catch(Exception e) {}
try { rs.close(); } catch(Exception e) {}
}
}
}.start();
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.