我遇到一个问题,我必须处理许多试图在数据库中编写字符串列表的线程。为了清楚起见,我们可以考虑将3个线程访问连接对象到DB。所有线程都将具有String。让坐的线程A有一个字符串叫做"第一个&#34 ;,线程B有"第二个&#34 ;,线程C有"第一个"。以下假设是我想以一种我将很快解释的方式保护的代码的关键部分。
writeToDataBase(String stringFromEachThread);
此方法可以由线程A和线程B同时执行,因为它们中包含不同的字符串值。当线程A从线程C执行时,我必须保护上面的代码,因为线程C也具有与线程A相同的字符串值。我需要使线程C等待,直到线程A完成执行。所以我遇到了java.util.Concurrent.Semaphore类,它将线程放入队列中。但在我的情况下,只有线程C必须放入队列而不是线程B.因为线程B有一个唯一的字符串值,所以它可以与线程A并行执行。信号量在执行线程A时阻塞线程C但是它也阻止了线程B.Below是我使用信号量实现这一目标的代码片段,目前还没有用。我已经在这个问题上待了一个多星期了。任何克服这一点的建议都深表赞赏。
Semaphore sm = new Semaphore(1, true);
... ...
public void lock(String str) throws InterruptedException {
if(!strAlreadExists()){
return;
}else{
sm.acquire();
}
}
答案 0 :(得分:1)
您似乎要求的是为单独的字符串设置单独的锁。你的代码片段似乎表明你认为你可以使用一个锁(无论它是一个信号量还是其他锁定机制),但不知何故没有它在不相关的线程之间共享。这不会奏效。
为单独的字符串实现单独锁定的一种方法是使用从字符串到锁定的映射。这是一个简单锁定的插图,而不是信号量:
声明私有地图字段:
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
代替同步。但重点仍然是你为每个不同的字符串获得一个单独的锁/信号量。
缺点是,如果你有很多字符串和很少的重复,地图会变得非常大。删除此地图中的任何内容都很困难,除非您计划一个单独的机制锁定所有线程,偶尔访问地图,并清理旧字符串。如果您使用信号量,则尤其如此,因为操作系统通常允许有限数量的信号量。
答案 1 :(得分:0)
听起来像Striped Executor Service会很合适。
这个神奇的线程池将确保具有相同stripeClass的所有Runnables将按照它们提交的顺序执行,但具有不同stripedClasses的StripedRunners仍然可以独立执行。他希望使用一个相对较小的线程池来为大量的Java NIO客户端提供服务,但这样的方式仍然可以按顺序执行runnable。
您可以使用String
作为密钥类,因此使用asme字符串编写的每个数据库都会保留iit的顺序,而其他所有数据库都可以并行运行。