Why is this multithreaded program getting stuck at infinite loop?

喜夏-厌秋 提交于 2021-02-10 14:53:17

问题


The below program is a simple threaded program . For some reason which i am not able to figure , its getting stuck at infinite loop of both produce() and consume() methods simultaneously in both the threads.

It produces the output for a few times and then there is no output at the console. So I presume its getting stuck at the loop.

My question is , since the loop depends on the value of the flag valueSet of the same object of Item class , valueSet can't be both true and false at the same time . So either of the produce() or cosume() method's loop should get false and the printing of output should continue.

But that's not happening here. So why is it getting stuck at the while loop if the condition depends on the flag variable which can only take true or false at a time ?

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() ; 
    }
}

Update :

When the while(valueSet) is stuck at infinite loop in one thread , shouldn't while(!valuSet) get out of loop and flip the valueSet? This would inturn cause while(valueSet) to come out of loop right ?

As per some answers it seems when while(valueSet) is stuck , the other thread is somehow not able to access valueSet . I don't understand how this is happening. Please explain your answer.

I see that using volatile for valueSet will fix it but i am not able to understand how without using it. It's causing the infinite loop even if it depends on one flag valueSet which can't be true and false at the same time.


回答1:


Basically, what you're trying to do here is to use valueSet as a boolean flag to synchronize the Consumer and Producer -- to make them work in turn. It is true that valueSet can only be true or false at one moment; however, it's not how the two threads (Consumer and Producer) view it.

We know that in Java, objects are stored on the heap; that is called the main memory. For each thread, however, for performance's sake, references to used objects are kept in a thread-specific cache. As in here, Producer and Consumer share one Item object which is stored on the heap; the field item.valueSet might be cached by each thread.

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

When, say, Consumer changes valueSet to false, it might or might not flush the new value to the main memory; similarly, when Producer checks valueSet, it might or might not try to read the newest value from the main memory. That's where the volatile keyword comes into play. When you set valueSet to be volatile, it ensures that both threads write/read the newest value to/from main memory.

Notice that the above summary is bascially known as the JVM Memory Model. It's a set of rules to define JVM's behaviour under multithreading situations.

If you try changing the following parts of your code:

    **volatile** boolean valueSet = false ; 
    **volatile** int item = 0 ;
    ...
    item.produce((int)(Math.random()*100)) ; // added parenthesis

You will see following output:

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



回答2:


For starters, you will need to make that shared variable volatile.

A thread is allowed to put its local variables in a register, so yes, one thread can see valueSet as false and the other can see it as true. At the same time. Make the variable volatile to force it to be read from memory each time.

However, this will not guarantee there are no other problems with the code. Synchronization can be tricky. But research volatile to get past the most likely cause.




回答3:


I could be wrong, as I'm not an expert at Java, but try writing your while loop like this:

public  void consume(){
    while(!valueSet) {
        System.out.println("Consumed : "  + item ) ; 
        valueSet = false ;
    }
}

Loops need content after wards inside curly brackets just like functions and for loops do. Your while loop could be getting stuck because it never gets to valueSet = false because it never actually properly initiates the loop.




回答4:


U should set the valueSet to volatile to make the variable visible for the two threads.




回答5:


This is due to Item's blocking with while().

If valueSet is set to True then while(valueSet); will wait forever, locking Item

Same for while(!valueSet);. This is a deadlock.




回答6:


You have to use volatile variable "valueSet" because:

  • "valueSet" is shared variable for Producer and Consumer

  • Your threads Producer & Consumer are using local copy of your variable "valueSet", which brings your code to a possibility that it might be true (OR false) for both at the same time

Also:

Using volatile ensures that

  • your threads Producer & Consumer will read shared volatile variable "valueSet" directly from main memory that will be latest (No possibility of valueSet being true Or false at the same time)

  • If there is a write going on your volatile variable "valueSet" by Producer or Consumer, and suddenly a read is requested by either of your threads, it is guaranteed that the write operation will be finished prior to the read operation.



来源:https://stackoverflow.com/questions/52694526/why-is-this-multithreaded-program-getting-stuck-at-infinite-loop

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!