使用信号量将特定线程放入队列

Using semaphore to put particular threads in queue

我面临一个问题,我必须处理许多试图在数据库中写入字符串列表的线程。为了清楚起见,让我们考虑 3 个正在访问数据库连接对象的线程。所有线程都将具有字符串。假设线程 A 有一个名为 "First" 的字符串,线程 B 有 "Second",线程 C 有 "First"。假设下面是代码的关键部分,我想以一种我将很快解释的方式保护它。

writeToDataBase(String stringFromEachThread);

线程A和线程B可以同时执行该方法,因为它们内部的字符串值不同。当线程 A 从线程 C 执行时,我必须保护上面的代码,因为线程 C 也具有与线程 A 相同的字符串值。我需要让线程 C 等待,直到线程 A 完成执行。所以我遇到了 java.util.Concurrent.Semaphore class 它将线程放入队列。但在我的例子中,只有线程 C 必须放入队列而不是线程 B。因为线程 B 具有唯一的字符串值,所以它可以与线程 A 并行执行。信号量在线程 A 执行时阻塞线程 C,但它阻塞线程 B,因为 well.Below 是我使用信号量实现此目的的代码片段,到目前为止还没有帮助。我已经解决这个问题一个多星期了。非常感谢任何克服这个问题的建议。

Semaphore sm = new Semaphore(1, true);

... ...

public void lock(String str) throws InterruptedException {
        if(!strAlreadExists()){
            return;
        }else{
            sm.acquire();
        }
}

听起来 Striped Executor Service 很合适。

This magical thread pool would ensure that all Runnables with the same stripeClass would be executed in the order they were submitted, but StripedRunners with different stripedClasses could still execute independently. He wanted to use a relatively small thread pool to service a large number of Java NIO clients, but in such a way that the runnables would still be executed in-order.

您可以使用 String 作为键 class 这样每个使用 asme 字符串的数据库写入都保留 iit 的顺序,而所有其他数据库可以 运行 并行。

您似乎要求的是 单独的锁 用于单独的字符串。您的代码片段似乎表明您认为您可以使用一个锁(无论是信号量还是其他锁定机制)但不知何故不会在不相关的线程之间共享它。那是行不通的。

为单独的字符串实现单独锁定的一种方法是拥有从字符串到锁定的映射。这是一个带有简单锁而不是信号量的插图:

声明私有映射字段:

private final ConcurrentMap<String, Object> lockMap = new ConcurrentMap<>();

创建一个方法来检索给定字符串的特定锁:

private Object getLock( String str ) {

    Object candidateLock = new Object();
    Object returnedLock = lockMap.putIfAbsent( str, candidateLock );
    return returnedLock == null ? candidateLock : returnedLock;

}

对于任何字符串,这将首先创建一个候选锁,并尝试将其放入并发映射中。

当任何线程尝试将候选者放入映射中时,映射中将已经存在针对该字符串的锁,或者候选锁成为针对该字符串的新锁。使用 putIfAbsent(...) 确保具有相同字符串的所有线程都获得相同的锁对象。如果映射中有锁,候选人将被丢弃以进行垃圾收集。

现在,要访问您的关键代码,您可以执行以下操作:

Object lock = getLock(str);

synchronized ( lock ) {
    writeToDataBase(str);
}

如果坚持使用信号量,可以用new Semaphore(...)代替new Object(),用acquire代替同步。但重点仍然是您为每个不同的字符串获得一个单独的 lock/semaphore。

缺点是如果您有很多字符串且重复次数很少,地图可能会变得非常大。很难从这个映射中删除任何东西,除非你计划一个单独的机制来锁定 所有访问映射的线程 ,偶尔,并清理旧字符串。如果您使用信号量,则尤其如此,因为通常操作系统允许的信号量数量有限。