问题
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