我想创建一个信号量来防止某个方法一次执行超过 1 次。
如果任何其他线程请求访问,它应该等待直到信号量被释放:
private Map<String, Semaphore> map;
public void test() {
String hash; //prevent to run the long running method with the same hash concurrently
if (map.contains(hash)) {
map.get(hash).aquire(); //wait for release of the lock
callLongRunningMethod();
} else {
Semaphore s = new Semaphore(1);
map.put(hash, s);
callLongRunningMethod();
s.release(); //any number of registered threads should continue
map.remove(hash);
}
}
问题:如何只用一个线程锁定信号量,然后释放它,以便任意数量的线程在释放后可以立即继续?
一些说明:
想象一下长时间运行的方法是一个事务方法。查看数据库。如果未找到条目,则会发送大量 XML 请求并将其保存到数据库。另外,可能会触发进一步的异步处理,因为这应该是数据的“初始获取”。然后从数据库返回对象(在该方法内)。如果DB条目已经存在,则直接返回实体。
现在,如果多个线程同时访问长时间运行的方法,所有方法都会获取大量的 XML(流量、性能),并且所有方法都会尝试将同一个对象持久保存到数据库中(因为长时间运行的方法是交易性的)。导致例如非唯一异常。再加上所有这些都会触发可选的异步线程。
当除一个线程外的所有线程都被锁定时,只有第一个线程负责持久化对象。然后,完成后,所有其他线程将检测到该条目已存在于数据库中,并只为该对象提供服务。
最佳答案
据我了解,您不需要在这里使用Semaphore
。相反,您应该使用ReentrantReadWriteLock
。此外,test
方法不是线程安全的。
下面的示例是使用 RWL 实现逻辑
private ConcurrentMap<String, ReadWriteLock> map = null;
void test() {
String hash = null;
ReadWriteLock rwl = new ReentrantReadWriteLock(false);
ReadWriteLock lock = map.putIfAbsent(hash, rwl);
if (lock == null) {
lock = rwl;
}
if (lock.writeLock().tryLock()) {
try {
compute();
map.remove(hash);
} finally {
lock.writeLock().unlock();
}
} else {
lock.readLock().lock();
try {
compute();
} finally {
lock.readLock().unlock();
}
}
}
在此代码中,第一个成功的线程将获取WriteLock
,而其他Thread
将等待写锁的释放。释放WriteLock
后,所有等待释放的Thread
将同时进行。
关于java - 如何释放信号量并让任何线程继续运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36789359/