【问题标题】:Does synchronized lock a Result Set object?同步是否锁定结果集对象?
【发布时间】:2016-05-31 17:38:42
【问题描述】:

我正在尝试对结果集进行多线程处理。我想确保每当我在多个线程之一中调用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 并确保其他线程不会干扰?

这是一个好方法吗?

【问题讨论】:

  • 我会让一个线程读取 ResultSet 并将任务添加到 ExecutorService。
  • 谢谢。我有一种感觉,我的方法是错误的。虽然有效,但感觉不对
  • 它将以这种方式工作。但不能保证顺序。
  • 我不想将您的注意力从 Nathan Hughes 的回答中移开,但您应该知道将synchronized 添加到您的getUsername() 方法中阻止其他线程使用rs。它所做的只是确保一次只允许一个线程进入getUsername()

标签: java multithreading jdbc resultset


【解决方案1】:

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

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

当线程可以同时进行时,多线程工作,请参阅Amdahl's Law。如果您有一种情况可以阅读 ResultSet 并使用结果创建提交给 ExecutorService 的任务(正如 Peter Lawrey 在评论中建议的那样),那么这将更有意义(只要这些任务可以独立工作并且不'不必彼此等待)。

【讨论】:

    【解决方案2】:

    我建议创建 ResultSet,然后将所有数据复制到 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 足够灵活,以在任何表中使用相同的类,而无需硬代码或固定名称。

    请记住,您将需要创建一个包装器和用于存储/读取数据的方法,并且这些方法应该是线程安全的。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-21
      • 2011-03-04
      • 1970-01-01
      相关资源
      最近更新 更多