2013-02-09 111 views
3

我正在写一些尖峰代码,它不会给我我期待的结果。java.sql.Connection隔离级别

我有一个表,基本上是行的计数器。其他表使用这些行来生成应该是唯一ID的内容。 当我运行下面的代码时,我所看到的是第一个到达select语句的线程将获得该行或表的锁,停止对唯一id值的所有读或写操作。然而第二个线程总是在第一个线程之前完成,因为它已经睡了1秒,因此它们都读取相同的值并写入相同的值,所以它只增量一次,而不是我除外的两倍。

我的代码有什么问题,或者是我对隔离级别的理解不正确?

我已经删除了锅炉板代码。标准的sql.Connection使用MySQL数据库。

private void incrementValue() { 

     connection 
       .setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); 

     statement = connection.createStatement(); 

     System.out.println(Thread.currentThread().getName() 
       + " doing select"); 
     resultSet = statement.executeQuery("select * from counter"); 
     System.out.println(Thread.currentThread().getName() 
       + "after select"); 
     if (counter++ == 0) { 
      Thread.sleep(1000); 
     } 
     String incrementedValue = getIncrementedValue(resultSet); 

     statement.executeUpdate("update counter set counter='" 
       + incrementedValue + "'"); 


} 

private String getIncrementedValue(ResultSet resultSet) throws SQLException { 
    String value = ""; 
    if (resultSet.next()) { 
     System.out.println(Thread.currentThread().getName() + "Value was " 
       + resultSet.getString(1)); 

     value = (new Integer(resultSet.getString(1)) + 1) + ""; 

    } 

    return value; 

} 

这是从主

public static void main(String[] args) { 
    DatabaseExample databaseExample = new DatabaseExample(); 

    Runnable runnable = new Runnable() { 

     @Override 
     public void run() { 
      DatabaseExample databaseExample = new DatabaseExample(); 
      databaseExample.incrementValue(); 
     } 
    }; 
    new Thread(runnable).start(); 

    databaseExample.incrementValue(); 
} 
+0

在这种情况下,必须在级别应用程序中进行锁定。只需使用关键字'synchronized'同步你的方法。请注意,这会影响应用程序的性能。 – 2013-02-09 15:00:13

回答

2

即使在SERIALIZABLE隔离级别调用,多个选择可以并行进行。如果要对select子句的行进行锁定,请使用select ... for update

参考文献:

http://dev.mysql.com/doc/refman/5.1/en/select.html

如果FOR UPDATE使用与使用页面或行锁的存储引擎,查询检验的行是写锁定,直到当前事务结束。使用LOCK IN SHARE MODE设置一个共享锁,允许其他事务读取已检查的行,但不更新或删除它们。

http://dev.mysql.com/doc/refman/5.1/en/set-transaction.html#isolevel_serializable

SERIALIZABLE

这个级别是像REPEATABLE READ,但是InnoDB的隐式转换所有纯SELECT语句SELECT ... LOCK IN SHARE MODE如果自动提交被禁用。

+0

JB Nizet:谢谢你的回复。我希望有一个非数据库特定的解决方案,因为这需要在不同供应商的数据库上运行,但似乎并不存在。 FOR UPDATE似乎可以在MySQL和Oracle以及使用WITH(UPDLOCK)的SQL Server上工作,同时捕获死锁异常并重新运行代码。 – Medu 2013-02-10 21:57:05