Java 用JNDI正确实现JDBC多线程

Java 用JNDI正确实现JDBC多线程,java,multithreading,jdbc,jndi,Java,Multithreading,Jdbc,Jndi,我正在做一个项目,在这个项目中我做了很多查询,时间是一个考虑因素,所以我想尝试并实现JDBC多线程。我不确定该怎么做 以下是我的第一份实施草案: Spring数据源Bean: private DataSource ds; @Resource(name="jdbc") public void setDataSource(DataSource ds) { this.ds = ds; } public void checkUsersMulti(List<User> users

我正在做一个项目,在这个项目中我做了很多查询,时间是一个考虑因素,所以我想尝试并实现JDBC多线程。我不确定该怎么做

以下是我的第一份实施草案:

Spring数据源Bean:

private DataSource ds;
@Resource(name="jdbc")
public void setDataSource(DataSource ds) {
    this.ds = ds;
}
  public void checkUsersMulti(List<User> users) throws Exception {
    if(users!= null || users.size() != 0) {
        ExecutorService executorService = Executors.newFixedThreadPool(20);
        Queue<User> queue = new ConcurrentLinkedQueue<>();
        queue.addAll(users);
        for (Useruser: users) {
            executorService.submit(new ProcessUser(queue));
        }
        executorService.shutdown();
        try {
            executorService.awaitTermination(1, TimeUnit.HOURS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
class ProcessUser implements Runnable {
    private final Queue<User> queue;

    public ProcessUser(Queue<User> queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            Connection conn = ds.getConnection();
            User user = null;
            while((user = queue.poll()) != null) {
                userDao.getUser(user , conn));
            }
            DbUtils.closeQuietly(conn);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public User retrieveUser(User user, Connection conn)  {
    PreparedStatement st = null;
    ResultSet rs = null;
    try {
        String sql = "SELECT firstname, lastname FROM users WHERE id= ?;

        st = conn.prepareStatement(sql);
        st.setString(1, user.getId());
        rs = st.executeQuery();

        while(rs.next()) {
            user.setFirstName(rs.getString("firstname"));
            user.setLastName(rs.getString("lastname"));
        }
    } catch (Exception e) {
        return null;
    }
    finally {
        DbUtils.closeQuietly(rs);
        DbUtils.closeQuietly(st);
    }
    return user;
}
 <Resource
    name="jdbc"
    auth="Container"
    type="javax.sql.DataSource"
    maxTotal ="25"
    maxIdle="30"
    maxWaitMillis ="10000"
    driverClassName="***Vendor Driver***"
    url="***Valid URL"
    username="***Valid Username***"
    password="***Valid Password***"
/>
初始化方法:

private DataSource ds;
@Resource(name="jdbc")
public void setDataSource(DataSource ds) {
    this.ds = ds;
}
  public void checkUsersMulti(List<User> users) throws Exception {
    if(users!= null || users.size() != 0) {
        ExecutorService executorService = Executors.newFixedThreadPool(20);
        Queue<User> queue = new ConcurrentLinkedQueue<>();
        queue.addAll(users);
        for (Useruser: users) {
            executorService.submit(new ProcessUser(queue));
        }
        executorService.shutdown();
        try {
            executorService.awaitTermination(1, TimeUnit.HOURS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
class ProcessUser implements Runnable {
    private final Queue<User> queue;

    public ProcessUser(Queue<User> queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            Connection conn = ds.getConnection();
            User user = null;
            while((user = queue.poll()) != null) {
                userDao.getUser(user , conn));
            }
            DbUtils.closeQuietly(conn);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public User retrieveUser(User user, Connection conn)  {
    PreparedStatement st = null;
    ResultSet rs = null;
    try {
        String sql = "SELECT firstname, lastname FROM users WHERE id= ?;

        st = conn.prepareStatement(sql);
        st.setString(1, user.getId());
        rs = st.executeQuery();

        while(rs.next()) {
            user.setFirstName(rs.getString("firstname"));
            user.setLastName(rs.getString("lastname"));
        }
    } catch (Exception e) {
        return null;
    }
    finally {
        DbUtils.closeQuietly(rs);
        DbUtils.closeQuietly(st);
    }
    return user;
}
 <Resource
    name="jdbc"
    auth="Container"
    type="javax.sql.DataSource"
    maxTotal ="25"
    maxIdle="30"
    maxWaitMillis ="10000"
    driverClassName="***Vendor Driver***"
    url="***Valid URL"
    username="***Valid Username***"
    password="***Valid Password***"
/>
更新:Tomcat JNDI连接池设置:

private DataSource ds;
@Resource(name="jdbc")
public void setDataSource(DataSource ds) {
    this.ds = ds;
}
  public void checkUsersMulti(List<User> users) throws Exception {
    if(users!= null || users.size() != 0) {
        ExecutorService executorService = Executors.newFixedThreadPool(20);
        Queue<User> queue = new ConcurrentLinkedQueue<>();
        queue.addAll(users);
        for (Useruser: users) {
            executorService.submit(new ProcessUser(queue));
        }
        executorService.shutdown();
        try {
            executorService.awaitTermination(1, TimeUnit.HOURS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
class ProcessUser implements Runnable {
    private final Queue<User> queue;

    public ProcessUser(Queue<User> queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            Connection conn = ds.getConnection();
            User user = null;
            while((user = queue.poll()) != null) {
                userDao.getUser(user , conn));
            }
            DbUtils.closeQuietly(conn);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public User retrieveUser(User user, Connection conn)  {
    PreparedStatement st = null;
    ResultSet rs = null;
    try {
        String sql = "SELECT firstname, lastname FROM users WHERE id= ?;

        st = conn.prepareStatement(sql);
        st.setString(1, user.getId());
        rs = st.executeQuery();

        while(rs.next()) {
            user.setFirstName(rs.getString("firstname"));
            user.setLastName(rs.getString("lastname"));
        }
    } catch (Exception e) {
        return null;
    }
    finally {
        DbUtils.closeQuietly(rs);
        DbUtils.closeQuietly(st);
    }
    return user;
}
 <Resource
    name="jdbc"
    auth="Container"
    type="javax.sql.DataSource"
    maxTotal ="25"
    maxIdle="30"
    maxWaitMillis ="10000"
    driverClassName="***Vendor Driver***"
    url="***Valid URL"
    username="***Valid Username***"
    password="***Valid Password***"
/>

但是,我偶尔会遇到这样的错误:

java.sql.SQLException:无法获取连接,池错误等待空闲对象超时


但是这个过程仍然如预期的那样完成——它可能在我的JNDI设置中。我更关心的是,这是否是实现这一目标的“正确”方式。我认为最好保留连接对象,这样它们就不必重新初始化。

这段代码有几个问题,最重要的是,我不确定这是什么:

   public void checkUsersMulti(List<User> users) throws Exception {
    if(users!= null || users.size() != 0) {
        ExecutorService executorService = Executors.newFixedThreadPool(20);
        Queue<User> queue = new ConcurrentLinkedQueue<>();
        queue.addAll(users);
        for (Useruser: users) {
            executorService.submit(new ProcessUser(queue));
        }
        executorService.shutdown();
        try {
            executorService.awaitTermination(1, TimeUnit.HOURS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
应该是:

    Connection conn = null;
    try {
        conn = ds.getConnection();
        User user = null;
        while((user = queue.poll()) != null) {
            userDao.getUser(user , conn));
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        DbUtils.closeQuietly(conn);
    }
只是为了确保在任何情况下都能关闭连接

此外,由于您显然正在使用一个底层连接池,您可能希望每次都尝试获取和返回连接


至于你的实际问题:你这样做的方式意味着前二十个runnables试图获得连接,然后保持它们很长时间。如果您的池被配置为使用提供的连接少于该值(比如15个),那么这很容易发生,因为前15个将成功获得连接,然后接下来的5个工作进程将继续运行可运行、尝试、失败、退出。或者,如果您的池中正好使用了20个连接,您可能会泄漏连接,因为您没有按上述方式正确关闭它们。

用户的大小是多少?对于任何大小的
用户
,都会出现问题吗?我的数据集现在大约有200个,我会尝试扩展和收缩它,看看它的行为是什么-我已经实现了@rmalchow的建议,但我很好奇它的行为如何。因此,通过这样做(User:users),我实际上创建了很多线程…其中20个线程实际上做了一些事情。所以我把它改为for(inti=0;I<20;I++)。我按照你的建议更新到了最后。保持连接的问题是它们可能超时并失败吗?我想更直接地说,保持连接的利弊是什么?我认为返回数据源获取连接的开销会稍微小一些……但我不知道会出现什么样的糟糕情况,或者为什么建议返回它。持续时间不是什么大问题,但正如@kiresays所写,没有太多开销。你运行过吗?进展如何?还有例外吗?你们知道你们的连接池设置吗?第一个测试看起来好多了,但我今天和下周要做一些广泛的测试。我将用我的连接设置更新OP。我会回来报告的-谢谢你的帮助!