synchronized 是否锁定结果集对象?

Does synchronized lock a Result Set object?

我正在尝试对结果集进行多线程处理。我想确保每当我在多个线程之一中调用 next() 时,所有其他线程都被锁定。这很重要,因为如果许多线程同时调用 next() 方法,这将导致跳过这些行。这是我做的

public class MainClass {
    private static ResultSet rs;

    public static void main (String [] args) {

        Thread thread1  = new Thread(new Runnable() {
            @Override
            public void run() {
                runWhile();
            }});
        Thread thread2  = new Thread(new Runnable() {
            @Override
            public void run() {
                runWhile();
            }});

        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();

        System.exit(0);
    }

    private static void runWhile () {
        String username = null;
        while ((username = getUsername()) != null) {
            // Use username to complete my logic 
        }
    }

    /**
     * This method locks ResultSet rs until the String username is retrieved. 
     * This prevents skipping the rows 
     * @return
     * @throws SQLException
     */
    private synchronized static String getUsername() throws SQLException {
        if(rs.next()) {
            return  rs.getString(1).trim();
        }
        else
            return null;
    }
}

这是使用 synchronized 的正确方法吗?它是否锁定 ResutSet 并确保其他线程不干扰?

这是一个好方法吗?

JDBC 对象不应在线程之间共享。这适用于连接、语句和结果集。这里最好的情况是 JDBC 供应商遵循规范并进行内部锁定,这样您就可以解决这个问题,在这种情况下,所有线程仍在尝试获取相同的锁,只有一个可以取得进展一次。这将比使用单个线程慢,因为除了从数据库中读取相同的工作之外,管理所有线程还会产生额外的开销。

(驱动程序完成的锁定可能是为了驱动程序的利益,因此提供商不必处理因用户滥用其软件而导致的竞争条件错误报告。它确实锁定并不一定意味着软件实际上应该被多个线程使用。)

多线程在线程可以同时取得进展时有效,请参阅 Amdahl's Law。如果您遇到可以读取 ResultSet 并使用结果创建提交给 ExecutorService 的任务(正如 Peter Lawrey 在评论中建议的那样),那么这将更有意义(只要这些任务可以独立工作并且不必须互相等待)。

我建议创建结果集,然后将所有数据复制到 DTO(数据传输对象)或 DAO(数据访问对象)中。在 DTO 或 DAO 上获得数据后,关闭您的 ResultSet、Statement 和 Connection。

创建 DTO/DAO 以按顺序存储记录、其字段和解析功能的非常简单的结构是这样的:

ArrayList<HashMap<String, Object>> table = new ArrayList<HashMap<String, Object>>();
HashMap<String, Object> record = new HashMap<String, Object>();
String field1 = "something";
Integer field2 = new Integer(45);
record.put("field1", field1);
record.put ("field2", field2);
table.add(record);

您可以(也许您应该)自动化并使 DTO/DAO 足够灵活以在任何 table 中使用相同的 class,无需硬代码或固定名称。

请记住,您需要为 storing/reading 数据创建包装器和方法,并且这些方法应该是线程安全的。

请记住,只有当您有足够的内存来存储 ResultSet 的所有记录时,此设计才有效。