java dbcp2 доступ к многопоточному соединению - PullRequest
0 голосов
/ 30 мая 2020

Я довольно хорошо знаком с использованием пула соединений на Tomcat и без проблем использовал его годами. Однако в настоящий момент я работаю над приложением основного метода, которому необходимо запускать одновременные потоки по соображениям производительности, и каждый из этих потоков должен иметь доступ к одной и той же базе данных. У меня есть код для работы, если я полностью исключу код базы данных и просто использую массивы для целей тестирования (например, многопоточность), однако, как только я добавляю обратно соединения с базой данных, первый поток берет блокировку, а другие потоки не т бегать вообще. Поиграли с 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();
        }
    }
}

1 Ответ

0 голосов
/ 01 июня 2020

@ user207421 был прав, что метод getDataSource () должен быть синхронизирован, и, конечно, я уже пробовал это; однако это все еще не решило мою проблему потока «0», не позволяющего другим потокам выполнять ход.

Я удалил все из моего кода, всех других библиотек и c .., пока не заставил его работать, а затем снова начал создавать его резервную копию, чтобы найти точку разрыва. Похоже, что главным определяющим фактором является размер ResultSet. Я попытался добавить дополнительное время thread.sleep в разных местах, однако единственное, что сработало, - это разбить запросы на меньшие ResultSets.

600 тыс. Наборов результатов, будет запущен только 1 поток, 1 тыс. Наборов результатов и 4 потока. Если ResultSets содержит только 100 строк, все 11 потоков будут выполняться. Обратите внимание: я тестировал это на системе с 16 процессорами с 8 ГБ памяти, выделенной для JVM (aws m5.4xlarge), поэтому аппаратные ресурсы не должны были быть фактором. Думаю, мне просто нужно разбить код на более мелкие куски.

Когда я изначально изучал это, я был удивлен отсутствием конкретных примеров кода c для этой конкретной c проблемы (независимо от размера 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();
            }
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...