java - 如何释放信号量并让任何线程继续运行?

标签 java multithreading semaphore

我想创建一个信号量来防止某个方法一次执行超过 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/

相关文章:

c - POSIX 信号量和 pthread 问题

java - 输入验证使用数组和 for 循环检查重复项和格式

java - 三元运算符不是一个语句

c# - waitany(Array) 实际上 'set' 是一个事件,还是只是返回有信号事件的索引

c++ - 类函数上的新线程

c - 使用 Semaphor、Mutex 和 PThread 的多线程

c - 在守护进程中释放信号量

java - 如何在 Apache Brooklyn 中获取资源

java - Spring MVC Hibernate Spring Security 登录

java - 显示 JWindow 一段时间