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
…直到稍后调用triggerPass(),线程B的作业才会执行


虽然我怀疑这会在实践中给我带来麻烦,但这一小差距是否能在正确性方面得到弥补?

我想我已经解决了这个问题,用
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来管理生产者/消费者交互。我对该数据结构的信心远远超过我编写的任何代码: