【发布时间】:2013-04-27 15:02:49
【问题描述】:
我正在尝试实现一个资源处理程序类,该类将资源(字符串,存储在数组中)分配给多个客户端,这些客户端可以尝试获取一组资源的锁并通过 lock 方法给出的 ID 解锁它们.
我正在尝试使用公平的 ReentrantReadWriteLock-s,每个资源一个。
我只看到客户端的日志。
有几个问题,有时线程不会停止请求和获取资源,有时会发生死锁,有时会释放锁失败。 任何提示表示赞赏。
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);
}
}
【问题讨论】:
-
有一件事让我感到困惑:字符串在 Java 中是不可变的。换句话说,无论加锁还是不加锁,你都不能写入字符串!但无论如何,让我们假设这些只是用作示例。在这种情况下,重要的是所有锁都以相同的顺序获取以避免死锁,验证 Set 保证这一点。然后,您不仅可以存储锁 ID,还可以存储拥有这些的线程 ID。这样你就可以确保线程不会释放它不拥有的任何东西,并且线程在释放之前不会再次锁定,这可能会再次由于顺序而死锁。
-
字符串没有被修改,它们只代表资源,客户端只是等待它们。我尝试了您的建议,而不是 Sets,我使用 Vector 来存储 resourceName 和锁对,并使用另一个 Vector 来存储分配,因此锁的获取顺序相同。在发布时,我也会检查线程。我还让 lockNum 变得易变。问题仍然存在。
-
我不认为使用向量有帮助,因为那个向量肯定没有排序。更糟糕的是,我相信在集合中你可以保证没有重复,而在向量中你没有。也就是说,代码中存在
lockNum的竞争条件:您递增它,第二个线程递增它,然后两者都使用相同的值。volatile在那里没有帮助,也许使函数同步会。顺便说一句:对整个资源管理使用单个锁可以解决您的问题,尽管您必须自己实现一些细粒度锁定的功能。 -
好吧,我做了一些修复,我注意到客户端可以等待一个随机的时间,可以是 0,然后他们无限期地等待。在这种情况下,尽管 ReentrantReadWriteLocks 资源仍然被阻塞,并且当它们是最后一个存活的线程时它们永远不会超时。我如何在课堂上提供帮助?
-
我不确定我是否理解你。以防万一,如果线程在终止之前没有返回资源,则等待该资源的任何其他线程将被永远阻塞。您的设计中没有任何东西可以阻止这一点,您需要客户正确行事。也就是说,您可以更新代码以反映当前状态吗?最后,你对那些想要返回他们不拥有的资源的客户太好了,抛出一个异常!
标签: java multithreading concurrency locking reentrantreadwritelock