下面的程序是一个简单的线程程序。由于某种原因,我无法弄清楚,它同时卡在两个线程中的produce()和consume()方法的无限循环中。

它会产生几次输出,然后控制台上没有任何输出。因此,我认为它陷入了循环。

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

但这不是在这里发生。那么,如果条件取决于只能一次为true或false的flag变量,为什么它会卡在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我不知道这是怎么回事。请解释您的答案。

我看到使用volatile替代valueSet可以解决此问题,但是如果不使用它,我将无法理解。即使它依赖于一个不能同时为真和为假的valueSet标志,它也会导致无限循环。

最佳答案

基本上,您要在此处执行的操作是将valueSet用作 boolean 标志,以使ConsumerProducer同步-使它们依次工作。的确,valueSet一次只能是true或false。但是,这不是两个线程(消费者和生产者)如何看待它的方式。

我们知道在Java中,对象存储在上;这就是主内存。但是,出于性能考虑,对于每个线程,对使用过的对象的引用都保留在特定于线程的缓存中。如此处所示,ProducerConsumer共享一个Item对象,该对象存储在堆中;每个线程可能会缓存item.valueSet字段。

 _______________    ______________
 |   Consumer    |  |   Producer   |
 |   _________   |  |   _________  |
 |  |         |  |  |  |         | |
 |  | Cache1  |  |  |  |  Cache2 | |
 |  | valueSet|  |  |  | valueSet| |
 |  |_________|  |  |  |_________| |
 |_______________|  |______________|
           | |              | |
           | |              | |
          _|_|______________|_|__
         |                       |
         |      MAIN MEMORY      |
         |      valueSet         |
         |_______________________|

例如,当ConsumervalueSet更改为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 : 83

Producing ....
Consumed : 83

Consuming !
Produced : 54

Producing ....
Consumed : 54

Consuming !
Produced : 9

Producing ....
Consumed : 9

Consuming !
Produced : 23

Producing ....
Consumed : 23

关于java - 为什么这个多线程程序陷入无限循环?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52694526/

10-11 20:02