Java 如何防止消费者出现微小竞争状况
在我的应用程序中,发生了两件事:Java 如何防止消费者出现微小竞争状况,java,concurrency,race-condition,Java,Concurrency,Race Condition,在我的应用程序中,发生了两件事: 各种线程产生作业 有一个函数(但不是一个持续运行的线程)消耗作业。此函数由生产者启动,但被锁定,因此它只运行一次 例如,生成一个作业: addJobToDatabase(...); triggerPass(); 这就是消费者功能的启动方式: public void triggerPass() { // prevent running more than once if (onceLock.tryLock()) { // onceLock
- 各种线程产生作业
- 有一个函数(但不是一个持续运行的线程)消耗作业。此函数由生产者启动,但被锁定,因此它只运行一次
addJobToDatabase(...);
triggerPass();
这就是消费者功能的启动方式:
public void triggerPass() {
// prevent running more than once
if (onceLock.tryLock()) { // onceLock is a ReentrantLock
try {
while (haveJobs()) {
doJobs();
}
} finally {
onceLock.unlock();
}
} else {
log.info("Pass triggered, but already running");
}
}
现在,这里有一个很小的竞赛条件。如果
- 线程A在期间离开了
,但尚未完成
onceLock.unlock()
- 线程B执行onceLock.tryLock(),返回false
虽然我怀疑这会在实践中给我带来麻烦,但这一小差距是否能在正确性方面得到弥补?我想我已经解决了这个问题,用
tryLock(1,TimeUnit.SECONDS)
替换了tryLock()
。不过,如果没有更多作业
和解锁()
之间的延迟超过1秒(谁知道数据库中发生了什么),这并不好,甚至不是万无一失的。不幸的是,在这种设计中,竞争条件是不可避免的。希望它是正确的吗?为什么不创建迭代器或队列之类的东西,其中读修改写操作是单个原子操作
public void triggerPass() {
Job job = null;
while ((job = jobIterator.next()) != null) {
doJob(job);
}
}
我想知道你为什么不使用生产者/消费者的封锁队列。我敢打赌这是你不想放弃的旧代码。@duffymo事实上我今天写的:P.队列在数据库中,因为它需要在崩溃和其他错误中生存。这是因为作业是需要实现的支付。在尝试获取锁时被阻止,然后如果
haveJobs()
返回false
立即从该方法返回,有什么不对?@BorisPavlovićHmm,这肯定会解决此特定问题,但可能会导致大量线程等待该锁,然后什么也不做(如果在一次处理过程中产生了许多作业)。您当然可以在数据库中对作业进行排队,但我会在Java代码中使用BlockingQueue来管理生产者/消费者交互。我对该数据结构的信心远远超过我编写的任何代码: