【问题标题】:Java ReentrantReadWriteLock - Correct Usage?Java ReentrantReadWriteLock - 正确用法?
【发布时间】:2014-05-21 14:26:34
【问题描述】:

我的 Web 应用程序存在一些并发问题,其中已完成对数据库的写入,并且可能还会同时进行读取。写入首先删除所有行,然后插入新行,因此可能会在数据库为空时完成读取,从而导致错误。我正在使用 ReentrantReadWriteLock,但想确保我正确使用它。任何修复将不胜感激。

private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

public static Schedule lookupSchedule()
{
    lock.readLock().lock();
    try
    {
        // read data from the database, manipulate it, return it
    }
    finally
    {
       lock.readLock().unlock();
    }
}

public static void setSchedule()
{
    Connection conn = DB.getConnection();
    lock.writeLock.lock();
    try
    {
       conn.openTransaction();
       // delete all the rows from the table
       // insert all the new rows into the table
       conn.committTransaction();
    }
    catch (Exception ex)
    {
       conn.rollbackTransaction();
    }
    finally
    {
        lock.writeLock.unlock();
    }
}

【问题讨论】:

  • 这对我来说很合适。你如何回收你的连接?
  • 连接在 commitTransaction() 或 rollbackTransaction() 方法中关闭,并且来自数据库池。
  • 您可能希望将数据库连接移到写入锁中,因为看起来您已经为查找方法设置了这种方式。否则你可以让lookupSchedule() 获得一个读锁,然后在它获得数据库连接之前,一堆写者可能会抓住所有的数据库连接然后阻塞写锁,从而导致死锁。
  • 你为什么还要使用锁?数据库的 ACID 属性应确保读取器在写入器事务之前使用数据库中的任何数据,即使写入器正在工作......
  • 另外,请参阅:stackoverflow.com/questions/11043712/… 理论上您可能会看到幻读行为...您可能只需要调整隔离级别。

标签: java concurrency java.util.concurrent reentrantreadwritelock


【解决方案1】:

就问题而言:您对ReentrantReadWriteLock 的使用是正确的。

关于您的问题:我认为您需要查看有效的事务隔离级别(并可能对其进行调整)。

希望这会有所帮助...

另一个 SO 线程中的隔离级别说明:what is difference between non-repeatable read and phantom read?

关于隔离级别的Java教程章节:http://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html#transactions_data_integrity

【讨论】:

  • 我原以为更新的事务性质也会保护它,但这不是我在实践中看到的。这些事务的隔离级别设置为 TRANSACTION_READ_UNCOMMITTED,从您的 cmets 判断,需要更改为 TRANSACTION_READ_COMMITTED 才能使其在没有锁的情况下正常工作。
  • 完全正确;请尝试设置隔离级别并让我们知道它是否适合您。此外,就像我之前提到的,通过适当的隔离,您可以只执行事务,甚至不必为 ReentrantReadWriteLock 锁而烦恼。数据库应该为你处理好它(即使你有多个作家)。
  • 根据java教程,似乎只有TRANSACTION_SERIALIZABLE会阻止幻读(参见答案中引用的章节中的表格)。我希望可以忍受...这是最昂贵的选择,但我认为这是唯一可以实现您想要的选择。您可能希望在另一个表中创建新数据,然后将它们移动到服务器端(存储过程)的原始表中以减少事务的跨度(或其他类似的技巧)。
  • 在隔离级别 TRANSACTION_READ_COMMITTED 下,lookupSchedule 方法永远不会看到空表,因为 delete&insert 在一个事务中,也就是说,lookupSchedule 不会看到空表(因为 delete没有插入就永远不会提交,lookupSchedule 永远不会看到未提交的更改。我认为 TRANSACTION_SERIALIZABLE 在这里没有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-09
  • 1970-01-01
  • 1970-01-01
  • 2016-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多