为什么这个多线程程序会陷入无限循环?

下面的程序是一个简单的线程程序。由于某种我无法想象的原因,它在两个线程中同时陷入了生产()和消费()方法的无限循环中。


它产生几次输出,然后在控制台上没有输出。所以我认为它陷入了循环。


我的问题是,由于循环取决于valueSetItem 类的同一对象的标志值,valueSet因此不能同时为真和假。因此,produce() 或 cosume() 方法的循环都应该为 false,并且输出的打印应该继续。


但这里不会发生这种情况。那么,如果条件取决于一次只能取真或假的标志变量,为什么它会卡在 while 循环中呢?


class Item{

    boolean valueSet = false ; 

    int item = 0 ; 


    public  void consume(){

        while(!valueSet) ;

        System.out.println("Consumed : "  + item ) ; 

        valueSet = false ;

    }


    public  void produce(int n ){

        while(valueSet);

        item = n ;

        System.out.println("Produced : "  + item ) ; 

        valueSet = true ;

    } 

}


class Producer implements Runnable{

 Item item ;

 Producer(Item itemobj){

     item = itemobj ; 

 }


 public void run(){

     while(true){

         System.out.println("\nProducing ....") ; 

     item.produce((int)Math.random()*100) ; 

     }

 }


}


class Consumer implements Runnable{

    Item item  ;

    Consumer(Item itemobj){item = itemobj ; }


    public void run(){

        while(true){

            System.out.println("\nConsuming !") ;

        item.consume() ; 


        }

    }

}



class Main{

    public static void main(String[] args) {

        Item item = new Item() ;

        Thread consumer = new Thread(new Consumer(item)) ; 

        Thread producer = new Thread(new Producer(item)) ;

        System.out.println("\nStarted producer and consumer threads : ") ; 

        consumer.start() ; 

        producer.start() ; 

    }

}

更新 :


当while(valueSet)一个线程卡在无限循环中时,不应该while(!valuSet)跳出循环并翻转valueSet? 这会反过来导致while(valueSet)跳出循环吗?


根据某些答案,似乎在while(valueSet)卡住时,另一个线程以某种方式无法访问valueSet。我不明白这是怎么回事。请解释你的答案。


我看到使用volatileforvalueSet会修复它,但我无法理解如何不使用它。即使它依赖于一个valueSet不能同时为真和假的标志,它也会导致无限循环。


慕码人8056858
浏览 233回答 3
3回答

噜噜哒

基本上,您在这里尝试做的是valueSet用作布尔标志来同步Consumer和Producer-- 使它们依次工作。真的,valueSet只能在一瞬间是真的或假的;然而,这不是两个线程(消费者和生产者)如何看待它。我们知道在 Java 中,对象存储在堆上;那就是所谓的主存。但是,对于每个线程,出于性能考虑,对所用对象的引用都保存在特定于线程的缓存中。如在这里,Producer和Consumer共享一个Item被存储在堆上对象; 该字段item.valueSet可能被每个线程缓存。 _______________    ______________   |   Consumer    |  |   Producer   |   |   _________   |  |   _________  |   |  |         |  |  |  |         | |   |  | Cache1  |  |  |  |  Cache2 | |   |  | valueSet|  |  |  | valueSet| | |  |_________|  |  |  |_________| |   |_______________|  |______________|           | |              | |           | |              | |          _|_|______________|_|__         |                       |         |      MAIN MEMORY      |          |      valueSet         |          |_______________________|例如,当Consumer更改valueSet为 时false,它可能会也可能不会将新值刷新到主内存;类似地,当Producer检查时valueSet,它可能会也可能不会尝试从主内存中读取最新值。这就是volatile关键字发挥作用的地方。当您设置valueSet为 时volatile,它确保两个线程向/从主内存写入/读取最新值。请注意,上面的总结基本上被称为JVM 内存模型。它是一组规则,用于定义多线程情况下 JVM 的行为。如果您尝试更改代码的以下部分:    **volatile** boolean valueSet = false ;     **volatile** int item = 0 ;    ...    item.produce((int)(Math.random()*100)) ; // added parenthesis您将看到以下输出:Started producer and consumer threads : Consuming !Producing ....Produced : 83Producing ....Consumed : 83Consuming !Produced : 54Producing ....Consumed : 54Consuming !Produced : 9Producing ....Consumed : 9Consuming !Produced : 23Producing ....Consumed : 23

阿晨1998

首先,您需要创建共享变量volatile。允许线程将其局部变量放在寄存器中,所以是的,一个线程可以将其valueSet视为 false,而另一个线程可以将其视为true. 同时。使变量volatile强制每次都从内存中读取。但是,这并不能保证代码没有其他问题。同步可能很棘手。但是研究volatile以克服最可能的原因。

桃花长相依

ü应该设置valueSet以volatile使两个线程变量可见。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java