为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

慕码人8056858 2021-11-03 16:49:07
下面的程序是一个简单的线程程序。由于某种我无法想象的原因,它在两个线程中同时陷入了生产()和消费()方法的无限循环中。它产生几次输出,然后在控制台上没有输出。所以我认为它陷入了循环。我的问题是,由于循环取决于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不能同时为真和假的标志,它也会导致无限循环。
查看完整描述

3 回答

?
噜噜哒

TA贡献1784条经验 获得超7个赞

基本上,您在这里尝试做的是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 : 83


Producing ....

Consumed : 83


Consuming !

Produced : 54


Producing ....

Consumed : 54


Consuming !

Produced : 9


Producing ....

Consumed : 9


Consuming !

Produced : 23


Producing ....

Consumed : 23


查看完整回答
反对 回复 2021-11-03
?
阿晨1998

TA贡献2037条经验 获得超6个赞

首先,您需要创建共享变量volatile

允许线程将其局部变量放在寄存器中,所以是的,一个线程可以将其valueSet视为 false,而另一个线程可以将其视为true. 同时。使变量volatile强制每次都从内存中读取。

但是,这并不能保证代码没有其他问题。同步可能很棘手。但是研究volatile以克服最可能的原因。


查看完整回答
反对 回复 2021-11-03
?
桃花长相依

TA贡献1860条经验 获得超8个赞

ü应该设置valueSetvolatile使两个线程变量可见。


查看完整回答
反对 回复 2021-11-03
  • 3 回答
  • 0 关注
  • 232 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信