Why does this broken program always work?

I tried to use examples from JCIP, and the program below did not work, but even if I execute it, I will say that it always works 20 times, which means that ready and number become visible, even if in this case it should be

 public class NoVisibility { private static boolean ready; private static int number; private static class ReaderThread implements Runnable { public void run() { while (!ready) Thread.yield(); System.out.println(number); } } public static void main(String[] args) { System.out.println(Runtime.getRuntime().availableProcessors()); //Number of Processor is 4 so 4+1 threads new Thread(new ReaderThread()).start(); new Thread(new ReaderThread()).start(); new Thread(new ReaderThread()).start(); new Thread(new ReaderThread()).start(); new Thread(new ReaderThread()).start(); number = 42; ready = true; } } 

On my machine, it always prints

 4 -- Number of Processors 42 42 42 42 42 

According to Listing 3.1 of JCIP It should sometimes print 0 or should never terminate it also suggest that there is no gaurantee that ready and number written by main thread will be visible to reader thread

Update I added 1000 ms to sleep in the main thread after all the threads stayed on the same output. I know the program is broken. And I expect her to act like that.

+2
source share
2 answers

This program is broken because ready and number must be declared volatile .
Due to the fact that ready and number are primitive variables, operations on them are atomic , but do not guarantee that they will be visible by other threads. <w> It seems that the scheduler starts threads after main , and therefore they are initialized by initializing number and ready . But this is just one planning.
If you add, for example, a sleep to main , to affect the scheduler, you will see different results.
So the book is true, there is no guarantee that Runnable work in separate threads to ever see the variable being updated, since the variables are not declared as volatile .

Update:
The problem here is that due to the lack of volatile compiler can freely read the ready field only once and reuse the cached value each time the loop is executed.
The program is inherently flawed. For streaming problems, the problem usually occurs when deploying the application in a field. From JSL :

For example, in the following (broken) piece of code, suppose this.done is a non-volatile logical field:

while (! this.done)
Thread.sleep (1000);

The compiler can freely read the this.done field only once and reuse the cached value each time the loop is executed. This would mean that the loop will never end, even if another thread changes the value of this.done.

+3
source

It is important to keep in mind that an incorrect parallel program can always work with the correct combination of JVM parameters, machine architecture, etc. This does not make it a good program, since it probably will not work in a different context: the fact that the concurrency problem is not displayed does not mean that it does not.

In other words, you cannot prove that the parallel program is correct in testing.

Back to your specific example, I see the same behavior that you are describing. But, if I delete the Thread.yield() instruction in a while loop, 3 out of 8 threads stop and print 42, and the rest don't, and the program never ends.

+1
source

Source: https://habr.com/ru/post/1435615/


All Articles