在Java中的线程中使用PreparedStatements是否正确?

Art*_*bin 8 java multithreading jdbc thread-safety

我还是一名兼职工作的大学生,因此我总是试图了解更好的做事方法.最近我不得不编写一个程序用于工作,其中程序的主线程将产生"任务"线程(对于每个数据库"任务"记录),这将执行一些操作,然后更新记录以表明它已完成.因此,我需要ThreadedTask对象中的数据库连接对象和PreparedStatement对象.

这大致是我最后写的,PreparedStatement每个线程创建一个对象是浪费吗?我认为静态PreparedStatments会造成竞争条件......

Thread A stmt.setInt();
Thread B stmt.setInt();
Thread A stmt.execute();  
Thread B stmt.execute();  

A版本永远不会被执行..

这个线程安全吗?创建和销毁PreparedStatement总是相同的对象并不是一个巨大的浪费?

public class ThreadedTask implements runnable {
    private final PreparedStatement taskCompleteStmt;

    public ThreadedTask() {
        //...
        taskCompleteStmt = Main.db.prepareStatement(...);
    }

    public run() {
        //...
        taskCompleteStmt.executeUpdate();
    }
}

public class Main {
    public static final db = DriverManager.getConnection(...);
}
Run Code Online (Sandbox Code Playgroud)

Thi*_*ilo 17

我认为在线程之间共享数据库连接(和准备好的语句)并不是一个好主意.JDBC不要求连接是线程安全的,我希望大多数驱动程序都不是.

为每个线程提供自己的连接(或者在每个查询的连接上进行同步,但这可能会破坏拥有多个线程的目的).

创建和销毁始终相同的PreparedStatement对象并不是一个巨大的浪费?

并不是的.大多数工作都发生在服务器上,如果您使用相同的SQL语句,它们将被缓存并重新使用.某些JDBC驱动程序还支持语句缓存,因此即使是客户端语句句柄也可以重用.

但是,通过使用批量查询而不是(或除了)多个线程,您可以看到实质性的改进.准备一次查询,并在一个大批量中运行它以获取大量数据.


Bal*_*usC 7

线程安全不是问题所在.所有看起来语法和功能都很好,它应该工作大约半小时.然而,资源泄漏是真正的问题.大约半小时后应用程序将崩溃,因为您在使用后从未关闭它们.数据库将迟早关闭连接本身,以便它可以将其声明回来.

也就是说,您不必担心准备语句的缓存.JDBC驱动程序和DB将负责此任务.而是担心资源泄漏并使您的JDBC代码尽可能稳固.

public class ThreadedTask implements runnable {
    public run() {
        Connection connection = null;
        Statement statement = null;
        try {
            connection = DriverManager.getConnection(url);
            statement = connection.prepareStatement(sql);
            // ...
        } catch (SQLException e) {
            // Handle?
        } finally {
            if (statement != null) try { statement.close(); } catch (SQLException logOrIgnore) {}
            if (connection != null) try { connection.close(); } catch (SQLException logOrIgnore) {}
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

要提高连接性能,请使用类似c3p0的连接池(顺便说一下,这并不意味着您可以更改编写JDBC代码的方式;始终在块中以尽可能短的范围获取关闭资源).try-finally


Tre*_*ins 5

最好使用连接池,并让每个线程向该池请求连接。在处理的连接上创建语句,记住要关闭它,并在完成后将其释放回池中。使用该池的好处是,如果发现线程并发成为问题,则可以轻松增加可用连接的数量。