使用JNDI正确实现JDBC多线程

时间:2016-01-21 03:48:18

标签: 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) 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();
        }
    }
}

用户DAO方法:

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;
}

更新:Tomcat JNDI连接池设置:

 <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: Cannot get a connection, pool error Timeout waiting for idle object

但是该过程仍然按预期完成 - 它可能在我的JNDI设置中。我更担心的是,这是实现这一目标的“正确”方法。我认为最好保留连接对象,这样就不需要重新初始化了。

1 个答案:

答案 0 :(得分:0)

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

   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();
        }

    }
}

应该这样做。如果我理解正确,你创建一个包含其中所有用户的队列,然后将其提交给执行者多次,就像有用户一样。这意味着:

  • 您在处理的用户数量方面受到限制,因为队列无法无限增长(这可能是@sabit khan所担心的)

  • 你必须为每个用户运行至少ONCE的runnable,尽管除非一些runnables因为错误而退出,前20个将继续运行直到队列为空,然后剩下的将被调用,参见队列为空,然后立即退出。

您应该限制启动的并行进程数。你有一个20的线程池,所以如果你正确关闭你的连接,应该永远不会有超过20个并发连接。你没有提到任何其他例外,但是这个:

    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();
    }

应该是:

    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);
    }

只是为了确保在任何情况下关闭连接。

另外,由于您显然正在使用底层连接池,因此您可能希望每次尝试获取并返回连接。

关于你的实际问题:你这样做的方式意味着前20个runnables试图获得连接,然后持有它们很长一段时间。如果您的池配置为使用提供的连接少于那个(假设为15),那么这很容易发生,因为前15个成功获得连接,然后接下来的5个工作人员将连续运行runnable,try,fail,exit 。或者您在池中使用了20个连接,您可能正在泄漏连接,因为您没有如上所述正确关闭它们。