2014-01-18 24 views
0

我有兩個線程調用一個方法,執行一個讀取,然後寫入一個mysql數據庫中的記錄。一些僞代碼:Java sql讀取+寫入 - 防止競爭條件?

public void incrementRowValue() { 
    Connection conn = ...; 
    try { 
     conn.setAutoCommit(false); 

     int value = conn.execute("select value from foo where id = 123"); 
     value += 1; 
     conn.execute("update foo set value = " + value + " where id = 123"); 

     conn.commit(); 
    } 
    catch (SQLException ex) { 
     conn.rollback(); 
    } 
    finally { 
     conn.close(); 
    } 
} 

所以,如果線程執行的差不多的時候,它看起來像讀+寫是不是作爲一個原子的項目執行。例如,如果起始值爲零,我的日誌顯示如果兩個線程幾乎同時執行,則結束值可以是一個而不是兩個。

是否有不同的制動水平,我可以用它來防止這種情況?如果有必要,我可以在java代碼中進行同步,但我可能會錯過數據庫級​​別爲我們提供的一大特性。

感謝

http://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html

回答

1

要做到這一點最簡單的方法就是敷SQL到一個語句,讓SQL服務器處理它,我不記得確切的語法斷手,但它會使用

conn.execute("update foo set value = (select value from foo where id = 123)+1 where id = 123"); 

的SQL服務器將鎖定該行(如果使用多行會鎖定他們全部)並自動執行操作:類似。

這也將是更快/更有效,因爲它不僅使單個數據庫調用,而不是兩個。儘管這樣的更新可能會被封裝到存儲過程中。

+0

實際上,我試圖執行將使該笨重的代碼 - 有什麼辦法來鎖定特定的行? – user291701

+0

您需要使用SQL事務。如果您正確設置並啓動一個事務,然後訪問事務中的一行,那麼它將鎖定該行直到事務結束。儘管如此,我仍然建議在一個存儲過程中完成整個事情。 –

+0

對不起,我不太喜歡 - 那麼我的微不足道的例子不應該那樣工作嗎?它只訪問事務內的單個行。 – user291701

0
You can manage database concurrency using isolation levels 

Isolation Level  Dirty Read Nonrepeatable Read Phantom Read 
READ UNCOMMITTED Permitted Permitted Permitted 
READ COMMITTED -- Permitted Permitted 
REPEATABLE READ  -- -- Permitted 
SERIALIZABLE 

SERIALIZABLE-it provide highest level of concurrency 

For details please refer : 

[http://www.oracle.com/technetwork/issue-archive/2005/05-nov/o65asktom-082389.html][1] 
1

這就是所謂的丟失更新問題。你可以使用Exclusive鎖。這會阻止其他事務讀取鎖定的數據。