这个多线程java代码中真的存在竞争条件吗?

这个多线程java代码中真的存在竞争条件吗?,java,multithreading,synchronization,race-condition,producer-consumer,Java,Multithreading,Synchronization,Race Condition,Producer Consumer,我在这篇文章中看到了一段我无法理解的代码(很可能是因为我是这方面的初学者)。该问题涉及到“一种明显的竞赛状态,有时制作人会结束比赛,发出信号,而消费工人会在消费完队列中的所有东西之前停止比赛。” 据我所知,只有在生产商决定不再在队列中添加任何项目后,才会对消费者设置“isRunning”。因此,如果使用者线程将isRunning视为FALSE,然后将inputQueue视为空,则将来不可能再向队列中添加任何内容。 很明显,我错了,遗漏了一些东西,因为回答这个问题的人都说这个问题的情景是不可能的。

我在这篇文章中看到了一段我无法理解的代码(很可能是因为我是这方面的初学者)。该问题涉及到“一种明显的竞赛状态,有时制作人会结束比赛,发出信号,而消费工人会在消费完队列中的所有东西之前停止比赛。”

  • 据我所知,只有在生产商决定不再在队列中添加任何项目后,才会对消费者设置“isRunning”。因此,如果使用者线程将isRunning视为FALSE,然后将inputQueue视为空,则将来不可能再向队列中添加任何内容。 很明显,我错了,遗漏了一些东西,因为回答这个问题的人都说这个问题的情景是不可能的。那么,有人能解释一下什么样的事件顺序导致了这种比赛状态吗

  • 事实上,我看到了另一个问题。例如,如果多个使用者线程看到生产者正在运行,并且说队列中有一个项目,那么许多线程可能会进入阻塞的“take”。如果制作者现在停止,而“take”中会出现一个线程, 其他线程将永远阻塞在“take”上。有趣的是,没有一个回答这个问题的人也指出了这个问题。那么,我对这一点的理解也可能是错误的

  • 我不想在那个问题上加上这个作为评论,因为这是一个老问题,我的疑问可能永远不会得到回答! 我在这里复制/放置该问题的代码,以供快速参考

    public class ConsumerWorker implements Runnable{
    
    private BlockingQueue<Produced> inputQueue;
    private volatile boolean isRunning = true;
    
    public ConsumerWorker(BlockingQueue<Produced> inputQueue) {
        this.inputQueue = inputQueue;
    }
    
    @Override
    public void run() {
        //worker loop keeps taking en element from the queue as long as the producer is still running or as 
        //long as the queue is not empty:
        while(isRunning || !inputQueue.isEmpty()) {
            System.out.println("Consumer "+Thread.currentThread().getName()+" START");
            try {
                Object queueElement = inputQueue.take();
                //process queueElement
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    //this is used to signal from the main thread that he producer has finished adding stuff to the queue
    public void setRunning(boolean isRunning) {
        this.isRunning = isRunning;
    }
    
    公共类ConsumerWorker实现可运行{
    私有阻塞队列输入队列;
    private volatile boolean isRunning=true;
    公共消费器工作者(阻止队列输入队列){
    this.inputQueue=inputQueue;
    }
    @凌驾
    公开募捐{
    //只要生产者仍在运行,工作循环就一直从队列中获取en元素,或者
    //只要队列不是空的:
    同时(正在运行| |!inputQueue.isEmpty()){
    System.out.println(“使用者”+线程.currentThread().getName()+开始”);
    试一试{
    Object queueElement=inputQueue.take();
    //进程队列元素
    }捕获(例外e){
    e、 printStackTrace();
    }
    }
    }
    //这用于从主线程发出信号,表示生产者已完成向队列添加内容
    公共void setRunning(布尔值isRunning){
    this.isRunning=isRunning;
    }
    
    我认为原始问题的OP可能意味着

    while(isRunning && !inputQueue.isEmpty()) 
    
    而不是

    while(isRunning || !inputQueue.isEmpty())
    
    前者显然产生了原始海报(*)中描述的问题,而后者确实存在您在第二点中描述的问题。这是一个简单的疏忽,但现在我们可以注意到这两种方法都是不正确的


    (*)并以某种方式假设队列永远不会为空。

    我认为原始问题的OP可能意味着

    while(isRunning && !inputQueue.isEmpty()) 
    
    而不是

    while(isRunning || !inputQueue.isEmpty())
    
    前者显然产生了原始海报(*)中描述的问题,而后者确实存在您在第二点中描述的问题。这是一个简单的疏忽,但现在我们可以注意到这两种方法都是不正确的


    (*)并以某种方式假设队列永远不会为空。

    你在两个问题上都是正确的。&&是正确的,
    |
    不是。至于第二个问题,答案是使用
    毒药丸
    或超时,这两种方法都可以解决问题


    对于我来说,我将创建一个新的同步类来聚合队列和
    isRunning
    变量,这样更改
    isRunning
    会导致
    take()中出现异常
    因此表示工作结束。

    你在这两个问题上都是正确的。是的
    &&
    是正确的,
    |
    不是。至于第二个问题,答案是使用
    毒药丸
    或超时,这两种方法都可以解决问题


    对于我来说,我将创建一个新的同步类来聚合队列和
    isRunning
    变量,这样更改
    isRunning
    会导致
    take()中出现异常
    这样就意味着工作结束了。

    我也有同样的想法。我确实检查了对该问题的“编辑”,看看OP是否更改了该问题并修改了运算符。但是,他没有。该运算符始终是| |。该qn中没有人对此提出质疑!这可能是因为没有必要,因为这两个表达式都是错误的,一个解决方案离子是必要的。一个简单的疏忽真的。我也有同样的想法。我确实检查了这个问题的“编辑”,看看OP是否改变了这个问题并修改了操作符。但是,他没有。这个操作符总是| |。那个qn中没有人质疑这个!这可能是因为它没有必要,因为两个表达式都是错误的,一个解决方案这是必要的。一个简单的疏忽。我实际上最喜欢@PeterLawrey的回应。但那就是我。我实际上最喜欢@PeterLawrey的回应。但那就是我。