2011-05-27 31 views
7

我正在尋找有效的系統,讓一系列讀/寫鎖按層次組織,以管理對分層組織資源的訪問。如果一個子樹被鎖定寫入,那麼在整個子樹中都不能獲得其他鎖,直到它被釋放;同樣,子樹中的寫入鎖應該防止鎖定在父項中。Java中用於等級重入讀/寫鎖定的策略是什麼?

這裏是我正在考慮的想法:

  • 使用Apache Commons Transaction。不幸的是,該項目自2008年3月以來一直未更新,並已非正式終止。某些API文檔似乎表明版本即將到來(1.3或2.0)將包含some kind of hierarchical locking,但源代碼無處可查,似乎我們無法再訪問其SVN存儲庫。

  • 使用一系列ReentrantReadWriteLocks,我會分層組織。我不是併發專家,我有點害怕自己做這件事。初步的想法似乎表明,即使在我可以嘗試鎖定一個子樹之前,我也不得不在整個管理ReentrantReadWriteLock的結構上使用外部鎖 - 因此,即使對於釋放鎖,我也必須使用外鎖......從java.util.concurrentjava.util.concurrent.atomic

  • 使用類來實現我的等級鎖定在一個更有效的方式比我能有一系列ReentrantReadWriteLock就做。

我準備走這最後的路,但我很驚訝,沒找到,更好地解決這個問題的任何退出庫。所以:

  • 我錯過了一些明顯的解決方案?
  • 或者這個問題只是特別難以妥善解決?
+0

遠離「原子」,正確理解和實施是非常微妙的。 – toto2 2011-05-28 14:19:08

+1

你的第二個解決方案似乎是正確的。當需要鎖定節點時,需要獲取全局鎖,以便可以檢查該節點的所有子節點都未鎖定,並且沒有鎖定其父節點。 – toto2 2011-05-28 14:32:45

+0

順便說一下,Commons Transaction是[available](http://commons.apache.org/transaction/download_transaction.cgi),但我不知道它對你是否有用。 – toto2 2011-05-28 14:36:46

回答

3

我不知道我是否很好地理解你的問題,正如你所說,當你鎖定一個子樹進行寫入時,整個結構被鎖定。 因此,簡單的解決方案是爲整個結構設置一個RW鎖。

順便說一句,java.util.concurrent.atomic不會幫助你超過RW鎖的樹。


如果你希望能夠鎖定的兄弟姐妹independly,你可以與第二溶液(鎖的樹,其中每個節點有其父的引用)去。

鎖定一個節點將使用它的寫鎖來鎖定它,並使用讀鎖來鎖定每個父節點。 因爲您無法獲取其寫入鎖定,因此鎖定已獲取讀取鎖定的子項時,父級無法在子級鎖定。 只有在沒有其他線程已經寫入鎖定任何父項的情況下,才允許鎖定子項。

上述的鎖是獨佔鎖。 (對於讀 - 寫鎖的另一個名稱是共享的獨佔鎖)

要添加共享鎖,每個節點還需要的原子整數,指示: 如果它是正的,間接的寫鎖定兒童的數量;如果它是否爲節點已被讀鎖定的次數。 此外,節點及其父母將被讀取鎖定,以避免父母獲取新的寫鎖。

的僞代碼:

Node { 
    // fields 
    parent: Node 
    lock: RWLock 
    count: AtomicInteger 
} 

public boolean trylocktree(node: Node, exclusive: boolean) { 
    if (exclusive) { 
     return trylocktree_ex(node, true); 
    } else { 
     return trylocktree_sh(node); 
    } 
} 
private boolean switch_count(i: AtomicInteger, diff: int) { 
    // adds diff to i if the sign of i is the same as the sign of diff 
    while (true) { 
     int v = i.get(); 
     if (diff > 0 ? v < 0 : v > 0) 
      return false; 
     if (i.compareAndSet(v, v + diff)) 
      return true; 
    } 
} 
private boolean trylocktree_ex(node: Node, writing: boolean) { 
    // check if a node is read-locked 
    if (!switch_count(node.count, 1)) 
     return false; 
    // lock using the lock type passed as an arg 
    if (!node.lock(writing).trylock()) { 
     node.count--; 
     return false; 
    } 
    // read-lock every parent 
    if (!trylocktree_ex(node.parent, false)) { 
     node.count-- 
     node.lock(writing).unlock(); 
     return false; 
    } 
    return true; 
} 
private boolean trylocktree_sh(node: Node) { 
    // mark as shared-locked subtree 
    if (!switch_count(node.count, -1)) 
     return false; 
    // get shared-lock on parents 
    if (!readlock_recursively(node)) { 
     node.count++; 
     return false; 
    } 
    return true; 
} 
private boolean readlock_recursively(node: Node) { 
    if (!node.lock(false).trylock()) 
     return false; 
    if (!readlock_recursively(node.parent)) { 
     node.lock(false).unlock(); 
     return false; 
    } 
    return true; 
} 

如果任何鎖定,因此無法獲取,那麼你解開你已鎖定並稍後重試(你可以使用全局條件變量,超時等,以實現這個)。

編輯:添加代碼讀鎖定/寫鎖定樹

+0

感謝您的補充說明。如果我鎖定孩子,我不確定是否已經足夠鎖定父母,因爲有人可能想要自行讀取鎖定父母並期望對所有孩子進行一致的閱讀),並且這樣做不起作用因爲寫鎖定的孩子會同時更新... – 2011-05-28 15:32:34

+0

我在這裏描述的鎖是這種結構的可重入鎖的實現,而不是讀寫鎖。我正在研究讀/寫鎖定解決方案。 – Kru 2011-05-28 18:19:58

+0

非常感謝您的更新。儘管如此,看來在你的實現中,如果我寫鎖定一個孩子,另一個線程仍然可以讀取鎖定父母,這是不幸的。看起來我們需要另一個計數器:寫鎖定後代的數量或類似的東西。 – 2011-05-30 14:37:46

0

我會去爲自己的解決方案,並採取由阿帕奇Apache Commons Transaction Algorithm給爲出發點的算法。 你可以使用ReentrantReadWriteLock,儘管通常這個鎖更適合一個生產者的模式 - 很多讀者可能不是你想要的東西。看起來你的鎖更像是一個普通的可重入互斥鎖。

相關問題