2012-09-25 62 views
2

我正在嘗試爲Java服務器中的競爭條件使用休眠進行臨時解決方法。代碼最初看起來是這樣的:在單個線程中導致死鎖的單休眠會話

s = sessionFactory.openSession(); 
Object o1 = dao.getMostRecentVersionOfObject(key, s); 
if (o1.performSomeTimeConsumingTask() == Looks.Ok) { 
    Transaction t = s.beginTransaction(); 
    dao.update(o1, s); 
    t.commit(); 
} 

原來的問題是,如果2級不同的線程得到相同的代碼塊在大致相同的時間,他們都將嘗試並獲得對象的同一版本,所以第二個將永遠失敗。由於存在多個負載均衡的服務器,所以真正解決這個問題的方法是使用分佈式鎖定系統來確保版本保持同步,並且事務不會踩在其他腳趾上。但是,由於用戶已經發現這是一個問題,並且針對此問題的長期解決方案需要時間來開發,所以我決定通過檢查對象是否在交易前更新來添加臨時黑客行爲開始。我創建了第二個會話來執行此版本檢查。如果有更新,我使用第二次會話來填充對象的字段,然後保存它。因此,新的代碼看起來有點像這樣:

Session s = sessionFactory.openSession(); 
Session transactionSession = s; 
Object o1 = dao.getMostRecentVersionOfObject(key, s); 
int version = o1.getVersion(); 
if (o1.performSomeTimeConsumingTask() == Looks.Ok) { 
    Session newerSession = sessionFactory.openSession(); 
    Object newerObject = dao.getMostRecentVersionOfObject(key, newerSession); 
    if (newerObject.getVersion() > version) { 
     // update fields... 
     transactionSession = newSession; 
    } 
} 
Transaction t = transactionSession.beginTransaction(); 
dao.update(o1, transactionSession); 
t.commit(); 

這個代碼在幾個環境,但在最重要的一個失敗,因爲報告的僵局。這發生在沒有第二個併發請求時 - 第二個會話被創建,檢查版本,然後在第一個會話執行提交事務時被忽略。我懷疑這個問題要麼是環境問題(但我不明白爲什麼會這樣),或者說hibernate不喜歡使用第二個會話,但這只是一個有教養的猜測。我特別困惑,因爲只有一個事務,爲什麼hibernate會報告這是一個死鎖。

關於此任何想法非常感謝!

+0

當報告死鎖時,不同的線程在哪裏?如果你得到StaleObjectException –

+0

它是在提交上,這裏的另一種解決方案是試圖重複事務/工作。 – xtro

回答

0

最初的問題是如果兩個不同的線程在大致相同的時間到達相同的代碼塊,他們都會嘗試獲取相同版本的對象,因此第二個線程總會失敗。

你的第二個解決方案仍然有同樣的問題。你這樣做,

  1. 獲取最新對象
  2. 更新域,如果版本不一樣
  3. 凱明更新的對象

當你在做第2步,其他線程將更新了目的。

如果你想避免這種情況發生,同步方法(至少首選)或做到以下幾點,

Session s = sessionFactory.openSession(); 
Object o1 =s.get('o1s class',key,new LockOptions(LockMode.PESSIMISTIC_WRITE)); 
//Do whatever you want with o1 
//Update o1 
//commit 

的想法是由db.See doc獲得了更新鎖得到的對象。您應該意識到使用樂觀鎖定的悲觀鎖定的優缺點。