1

Why the pool does not set to 5 in the f() method that is called by another thread?

public class Main {        
        private static int pool = 5;        
        public static void main(String... arg) {        
            Th t = new Th();
            Thread thread = new Thread(t);
            thread.start();

            while (true) {        
               if (pool >= 0)
                    System.out.println(pool--);
            }       
        }

        static void f() {
            pool = 5;
            System.out.println("main.Main.f:pool=" + pool);
        }       
    }
    class Th implements Runnable {        
        @Override
        public void run() {
            while (true) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                Main.f();        
            }
        }
    }

Output:

5
4
3
2
1
0
main.Main.f:pool=5
main.Main.f:pool=5
main.Main.f:pool=5
main.Main.f:pool=5

I can fix it declaring pool as volatile but I can't get why it works 8(

  • 1
    Possible duplicate of [Do you ever use the volatile keyword in Java?](http://stackoverflow.com/questions/106591/do-you-ever-use-the-volatile-keyword-in-java) – shmosel May 17 '16 at 18:26
  • @shmosel, not a duplicate. This question is not about volatility. See javadba's answer, below. – Solomon Slow May 17 '16 at 18:48
  • Question is about value visibility across the threads. And that is all about volatility (or synchronisation). – Nikem May 17 '16 at 19:29
  • @Nikem: it seems to me the question is about unspecified behavior. – Nathan Hughes May 17 '16 at 19:35
  • There are many issues with the code and various things that explain what you're seeing. Start by reading about the [Java Memory Model](https://en.wikipedia.org/wiki/Java_memory_model) (link is to wikipedia, Google will give you more). Choice quote: _The major caveat of this is that as-if-serial semantics do not prevent different threads from having different views of the data_. Essentially, there is no requirement that one thread will see changes made by another, unless certain synchronization is enforced by various means. – davmac May 17 '16 at 19:39
  • @NathanHughes why unspecified? The meaning of volatile keyword is very well specified. As are the consequences of its absencee. – Nikem May 18 '16 at 06:23

2 Answers2

2

A value written by one thread won't always be read by another. There needs to be a "memory barrier" in order to guarantee visibility, and declaring a field as volatile is one way to obtain that guarantee.

Loosely speaking, you have to ask for visibility if you want it, otherwise the runtime is free to skip it as an optimization. For example it might use a register to store the main thread's value of pool instead of updating a field in an object out in the heap. Your code doesn't indicate that other threads should be able to tell the difference, so the runtime is allowed to perform the optimization.

erickson
  • 265,237
  • 58
  • 395
  • 493
0

The behavior makes sense: your main() has no sleep() in it - so it is able to execute the decrement five times in quick succession. Then after 1000 milliseconds - and every second after - the f() is called to set the pool to 5.

WestCoastProjects
  • 58,982
  • 91
  • 316
  • 560
  • And after each second when the pool is set back to 5, why doesn't the main thread count down again in quick succession? That's the point of this question. – erickson May 17 '16 at 18:49
  • @erickson Ah you are correct - and thus the reason for the `volatile`. – WestCoastProjects May 17 '16 at 18:53
  • @erickson Using [AtomicInteger Java API](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicInteger.html) will make changes to the pool in the order the threads access the AtomicInteger method. Which will allow for only one write at a time any simultaneous write issues. – Mr00Anderson May 17 '16 at 18:57