2013-02-09 149 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