我在一个应用程序中遇到了一个问题,其中两个具有相同参数的线程运行,发生了冲突。一种解决方案是使用同步块,但由于该问题只发生在具有相同参数的线程上,因此效率非常低。我想到的一种方法是使用并发映射将参数组合存储为键,将对象存储为值,每当线程开始操作时,它首先检查映射是否包含键(参数组合),如果包含,它将等待存储该组合的对象。线程最后将从映射中删除该对象,并对其调用notify。这种方法的问题是为相同的参数组合生成相同的对象。例如:如果thread1插入到map中,并调用notify并删除它,thread2可能会从等待中出来,但其他线程永远不会出来,因为对象从map中丢失了。
是否有另一种优雅的方法来解决这个问题?
创建参数组合池。每次需要保证一个线程正在运行时,调用Pool.acquire(params)
。
像这样使用:
用良好的hashCode
和equals
包裹对象中的参数。
synchronized(pool) {
pool.acquire(myParams);
}
// do the work
synchronized(pool) {
pool.release(myParams);
}
池:
class Pool {
Set<Params> lockedParams;
// In Pool - note that this is synchronized!
void acquire(Params params) {
Params lock = lockedParams.get(params);
if(lock != null) {
// Locked by another thread
lock.wait();
}
lockedParams.add(params);
}
void release(Params params) {
Params lock = lockedParams.remove(params);
lock.notifyAll();
}
}
这是一个想法。您需要为现实生活中的情况添加一些错误处理(finally
中的release()
,在本例中Pool.release()
中的lock
处理null,等等)。
调用notifyAll
而不是notify
,这样所有等待的线程都将被通知。为什么要从map中移除同步对象?尝试获取对象上的锁,如果线程成功,则进入临界区,否则开始等待。
您可以为等待添加超时,因此,如果线程在一段时间内无法获得锁,则它失败。这样你的代码就不会陷入死锁。
如果您可以将参数(或它们的子集)表示为字符串,则可以在interned
字符串上同步