java - 为什么这个多线程程序会陷入死循环?

标签 java multithreading performance parallel-processing producer-consumer

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

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

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

但这并没有发生在这里。那么,如果条件取决于一次只能取 true 或 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) 是否应该跳出循环并翻转 值集?这反过来会导致 while(valueSet) 跳出循环,对吗?

根据一些答案,当 while(valueSet) 被卡住时,另一个线程似乎无法访问 valueSet我不明白这是怎么回事。请解释您的答案。

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

最佳答案

基本上,您在这里尝试做的是使用 valueSet 作为 boolean 标志来同步 ConsumerProducer --让他们轮流工作。的确,valueSet 只能在某一时刻为真或假;但是,这不是两个线程(消费者和生产者)如何看待它。

我们知道在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/

相关文章:

Java - 从 OkHttp 异步 GET 检索结果

c - PTHREAD_CANCELED 和线程的启动函数返回值有什么问题

ios - NSOperationQueue 的取消在 swift 中不起作用

performance - 通过数组操作高效搜索包含子排列的排列?

python - 获取多个日期时间对的日期范围

java - 来自 java.util.zip.Inflater.init(Native Method) 的奇怪 java.lang.OutOfMemoryError

java:结合了instanceof和cast?

java - 如何在 RetroFit 中使用 Gson 转换器?

c# - 在写入文件时复制文件线程安全吗?

c# - 循环访问数据库