3

我想實現的是分配資源(字符串,保存在一個數組)到多個客戶端,可以試圖獲取一組資源的鎖,並給出一個ID解鎖的資源處理類通過鎖定方法。資源經理ReentrantLocks

我想使用公平的ReentrantReadWriteLock-s,每個資源的一個。

我只看到客戶端的日誌。

有幾個問題,有時線程不會停止請求和獲取資源,有時會發生死鎖,有時會發生releaseLock失敗。 任何提示讚賞。

public class ResHandler { 

//ID-s of the granted resource lists 
private static long lockNum = 0; 

//Resources are identified by strings, each client has a list of demanded resources 
//we store these when granted, along with an ID 
private static ConcurrentHashMap<Long, Set<String>> usedResources 
    = new ConcurrentHashMap<Long, Set<String>>(); 

//We store a lock for each resource 
private static ConcurrentHashMap<String, ReentrantReadWriteLock> resources 
    = new ConcurrentHashMap<String, ReentrantReadWriteLock>(); 

//Filling our resources map with the resources and their locks 
static { 
    for (int i = 0; i < SharedValues.RESOURCE_LIST.length; ++i) { 
     String res = SharedValues.RESOURCE_LIST[i]; 
     //Fair reentrant lock 
     ReentrantReadWriteLock lc = new ReentrantReadWriteLock(true); 
     resources.put(res, lc); 
    } 
} 

//We get a set of the required resources and the type of lock we have to use 
public static long getLock(Set<String> mNeededRes, boolean mMethod) { 
    //!!! 
    if (mMethod == SharedValues.READ_METHOD) { 

     //We try to get the required resources 
     for (String mn : mNeededRes) 
      resources.get(mn).readLock().lock(); 

     //After grandted, we put them in the usedResources map 
     ++lockNum; 
     usedResources.put(lockNum, mNeededRes); 
     return lockNum;   
    } 

    //Same thing, but with write locks 
    else { 

     for (String mn : mNeededRes) 
      resources.get(mn).writeLock().lock(); 

     ++lockNum; 
     usedResources.put(lockNum, mNeededRes); 
     return lockNum;   
    } 
} 

//Releasing a set of locks by the set's ID 
public static void releaseLock(long mLockID) { 
    if (!usedResources.containsKey(mLockID)) { 
     System.out.println("returned, no such key as: " + mLockID); 
     return; 
    } 

    Set<String> toBeReleased = usedResources.get(mLockID); 

    //Unlocking every lock from this set 
    for (String s : toBeReleased) { 
     if (resources.get(s).isWriteLockedByCurrentThread()) 
      resources.get(s).writeLock().unlock(); 
     else 
      resources.get(s).readLock().unlock(); 
    } 

    //Deleting from the map 
    usedResources.remove(mLockID); 
} 
} 
+0

還有一兩件事是我百思不得其解:字符串在Java不變。換句話說,鎖定或不鎖定,無論如何你都不能寫入字符串!但無論如何,讓我們假設這些只是作爲例子。在這種情況下,重要的是所有的鎖都是以相同的順序獲取以避免死鎖,請驗證Set <>是否可以保證這一點。然後,您不僅可以存儲鎖ID,還可以存儲擁有這些ID的線程ID。這樣你就可以確保一個線程不釋放任何它不擁有的東西,並且一個線程在釋放之前不會再次鎖定,這可能會再次因爲該命令而死鎖。 – 2013-04-27 16:07:34

+0

字符串不被修改,它們只代表資源,客戶端只是等待它們。 我嘗試了你的建議,而不是集合,我使用一個Vector來存儲resourceName和鎖對,另一個Vector來存儲分配,所以鎖以相同的順序獲取。在釋放我也檢查線程。我也使lockNum變得不穩定。 問題仍然存在。 – user1875619 2013-04-27 18:26:25

+0

我不認爲使用矢量有幫助,因爲那個確實沒有排序。更糟糕的是,我相信一套你有保證,沒有重複,而在一個載體中,你沒有。也就是說,在代碼中是一個使用'lockNum'的競爭條件:你增加它,第二個線程遞增它,然後使用相同的值。 'volatile'在這裏沒有幫助,可能會使函數同步。順便說一句:對整個資源管理使用單個鎖將解決您的問題,但您必須自己實施一些細粒度鎖定功能。 – 2013-04-27 19:15:37

回答

0

更新的解決方案,不妨一試:

public class ResHandler { 

private static AtomicLong lockNum = new AtomicLong(0); 
private static Map<Long, Set<String>> usedResources = new ConcurrentHashMap<Long, Set<String>>(); 
private static final Map<String, ReentrantReadWriteLock> resources = new ConcurrentHashMap<String, ReentrantReadWriteLock>(); 
// "priorityResources" to avoid deadlocks and starvation 
private static final Map<String, PriorityBlockingQueue<Long>> priorityResources = new ConcurrentHashMap<String, PriorityBlockingQueue<Long>>(); 

static { 
    for (int i = 0; i < SharedValues.RESOURCE_LIST.length; ++i) { 
     String res = SharedValues.RESOURCE_LIST[i]; 
     ReentrantReadWriteLock lc = new ReentrantReadWriteLock(true); 
     resources.put(res, lc); 
     priorityResources.put(res, new PriorityBlockingQueue<Long>()); 
    } 
} 

public static long getLock(Set<String> mNeededRes, boolean mMethod) { 
    long lockNumLocal = lockNum.addAndGet(1); 
    for (String mn : mNeededRes) { 
     priorityResources.get(mn).offer(lockNumLocal); 
    } 
    boolean tryLockResult; 
    List<String> lockedList = new ArrayList<String>(); 
    boolean allLocked = false; 
    while (!allLocked) { 
     allLocked = true; 
     for (String mn : mNeededRes) { 
      if (lockedList.contains(mn) == true) { 
       continue;//because we already have the lock 
      } 
      try { 
       if (mMethod == SharedValues.READ_METHOD) { 
        tryLockResult = resources.get(mn).readLock().tryLock(1, TimeUnit.MILLISECONDS); 
       } else { 
        tryLockResult = resources.get(mn).writeLock().tryLock(1, TimeUnit.MILLISECONDS); 
       } 
      } catch (InterruptedException ex) { 
       Logger.getLogger(ResHandler.class.getName()).log(Level.SEVERE, null, ex); 
       tryLockResult = false; 
      } 

      if (tryLockResult) { 
       lockedList.add(mn); 
      } else { 
       allLocked = false; 
       for (int i = lockedList.size() - 1; i >= 0; i--) { 
        //if the lock failed, all previous locked resources need to be released, but only if they will be used by higher priority lock operations 
        if (priorityResources.get(lockedList.get(i)).peek() != lockNumLocal) { 
         if (mMethod == SharedValues.READ_METHOD) { 
          resources.get(lockedList.get(i)).readLock().unlock(); 
         } else { 
          resources.get(lockedList.get(i)).writeLock().unlock(); 
         } 
         lockedList.remove(i); 
        } 
       } 
       break; 
      } 
     } 
    } 
    usedResources.put(lockNumLocal, mNeededRes); 
    for (String mn : mNeededRes) { 
     priorityResources.get(mn).remove(lockNumLocal); 
    } 
    return lockNumLocal; 
} 

public static void releaseLock(long mLockID) { 
    if (!usedResources.containsKey(mLockID)) { 
     System.out.println("returned, no such key as: " + mLockID); 
     return; 
    } 

    Set<String> toBeReleased = usedResources.get(mLockID); 

    //Unlocking every lock from this set 
    for (String s : toBeReleased) { 
     if (resources.get(s).isWriteLockedByCurrentThread()) { 
      resources.get(s).writeLock().unlock(); 
     } else { 
      resources.get(s).readLock().unlock(); 
     } 
    } 

    //Deleting from the map 
    usedResources.remove(mLockID); 
} 

}

+0

我試過你的代碼,但程序再次被凍結,並且比以前更早。此外,大多數客戶現在還沒有獲得所需的資源,但如果他們這樣做,我甚至無法看到我應該看到的時間流逝。你有沒有試過你的代碼?您是否運行該程序? – user2821898 2013-10-12 10:44:28

+0

對不起,這是一個瘋狂的猜測。現在看看它,是的,你在那裏陷入嚴重的僵局。因爲你試圖鎖定一個集合(而不是單個項目),他們遲早會陷入僵局。 – 2013-10-13 21:06:08

+0

我嘗試了多次新代碼,程序只被凍結了一次。所以它更好,最好但不完美:)截止日期是明天。我認爲你贏得了你應得的分數。 – user2821898 2013-10-17 22:28:29

0

我假設,不同的客戶端可以從不同的線程調用GETLOCK。如果是這樣,那麼第一個問題是對lockNum的訪問不同步。兩個線程可能同時調用getLock,因此根據時間的不同,它們可能都會返回相同的鎖定編號。這可以解釋爲什麼釋放鎖有時會失敗。

如果你能解決這個問題,應該更容易計算出發生了什麼。

+0

好的,謝謝。我會嘗試先解決它! – user2821898 2013-10-12 22:21:17

0

爲了避免死鎖的資源必須以相同的順序被收購,所以你必須在週期執行鎖之前進行排序Set<String> mNeededRes。排序方法並不重要。

這大大Chapter10. Avoiding Liveness HazardsJava Concurrency In Practice Brian Göetz說明。

標識recoment您刪除getLockreleaseLock或讓他們私人。並將所有的邏輯都包含在Runnable中。如果你控制所有的鎖定,就沒有辦法忘記釋放它們。讓這樣的事情:

public void performMethod(List<String> mNeededRes, boolean mMethod, Runnable r){ 
    List sortd = Collections.sort(mNeededRes); 
    try{ 
     getLock(mNeededRes, mMethod); 
     r.run(); 
    }finally { 
     releaseLock(mNeededRes); 
    } 
} 
+0

是的,但ResHandler類必須包含鎖定和釋放的接口。任務包括它。 – user2821898 2013-10-17 22:21:47

+0

然後,只需要對鎖和發佈進行排序。這是避免死鎖的必要條件。閱讀我提供的鏈接。 – Mikhail 2013-10-18 06:10:54

+0

好吧,我也有一本PDF格式的書,所以我會:) – user2821898 2013-10-18 09:26:45

2

有幾個問題在程序上會是對拘留所和錯誤的原因:

  • 一般:聲明全局變量決賽。你不想不小心惹上他們。此外,這允許您將它們用作同步對象。

  • 長不保證是原子,也不是運算符++。一個32位的JVM必須分兩步寫這個,因此理論上可以在系統中產生一個主要的特性。更好地使用AtomicLong。

  • getLock不是線程安全的。例如:

線程A調用GETLOCK資源1,3,5
線程B同時調用GETLOCK資源2,5,3上1,3
線程A被授予鎖,然後它被放置在暫停
線程B被授予鎖2,5然後被放置在從線程B暫停
線程A現在等待5和線程B現在對3等待從線程A.
死鎖。

請注意,發佈方法不需要同步,因爲它不能鎖定任何其他線程。

  • ++lockNum如果同時調用這個全局變量,會導致兩個線程混淆它們的鎖定值。

這裏處於工作狀態的代碼:

private static final AtomicLong lockNum = new AtomicLong(0); 
    private static final ConcurrentHashMap<Long, Set<String>> usedResources = new ConcurrentHashMap<Long, Set<String>>(); 
    private static final ConcurrentHashMap<String, ReentrantReadWriteLock> resources = new ConcurrentHashMap<String, ReentrantReadWriteLock>(); 

    static { 
    for (int i = 0; i < SharedValues.RESOURCE_LIST.length; ++i) { 
     String res = SharedValues.RESOURCE_LIST[i]; 
     ReentrantReadWriteLock lc = new ReentrantReadWriteLock(true); 
     resources.put(res, lc); 
    } 
    } 

    public static long getLock(Set<String> mNeededRes, boolean mMethod) { 
    synchronized (resources) { 
     if (mMethod == SharedValues.READ_METHOD) { 
     for (String mn : mNeededRes) { 
      resources.get(mn).readLock().lock(); 
     } 
     } else { 
     for (String mn : mNeededRes) { 
      resources.get(mn).writeLock().lock(); 
     } 
     } 
    } 
    final long lockNumber = lockNum.getAndIncrement(); 
    usedResources.put(lockNumber, mNeededRes); 
    return lockNumber; 
    } 

    public static void releaseLock(final long mLockID) { 
    if (!usedResources.containsKey(mLockID)) { 
     System.out.println("returned, no such key as: " + mLockID); 
     return; 
    } 

    final Set<String> toBeReleased = usedResources.remove(mLockID); 

    for (String s : toBeReleased) { 
     final ReentrantReadWriteLock lock = resources.get(s); 
     if (lock.isWriteLockedByCurrentThread()) { 
     lock.writeLock().unlock(); 
     } else { 
     lock.readLock().unlock(); 
     } 
    } 
    }