Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multithreading - Why does the following program behave this weirdly?

The outline of the program:
We have two threads (t1 and t2) that write an integer value, then flush the written value to RAM.
Another thread (t3) checks whether the value coincidences with the one written by t1or t2, and if not, prints it.

public class Container
{
     int a;
     volatile boolean b;


    public static void main(String[] args)
    {
        Container container = new Container();

        Thread t1 = new Thread()
        {
            @Override
            public void run()
            {
                for (;;)
                {
                    container.a = 409;
                    container.b ^= container.b;

                }
            }
        };

        Thread t2 = new Thread()
        {
            @Override
            public void run()
            {
                for (;;)
                {
                    container.a = 102;
                    container.b ^= container.b;
                }
            }
        };

        Thread t3 = new Thread()
        {
            @Override
            public void run()
            {
                try
                {
                    Thread.sleep(100);
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                for (;;)
                {
                    if (container.a != 409 && container.a != 102 )
                        System.out.println(container.a);
                }
            }
        };

        t1.start();
        t2.start();
        t3.start();
    }
}

What I thought would happen:
Since a isn't volatile, I thought t3 would cache a and just never print anything.

What actually happens:
For a second or so (no matter long you let t3sleep), it prints in rapid succession either 102 or 409. Then, printing stops (forever).

What exactly happens here?

like image 576
Sudix Avatar asked Sep 18 '18 15:09

Sudix


1 Answers

container.a not being volatile doesn't mean it's mandatorily cached by t3. All it means is that there are no guarantees whether it will be or not.

The reason it is free to print any value is the time-of-check-to-time-of-use problem here:

if (container.a != 409 && container.a != 102 )
   System.out.println(container.a);

As for why this exact behaviour on the exact environment you tested it in, we can only guess. But my money would be on the theory that while the code is run as interpreted, it will go and read container.a every time, but once it's compiled to native code by Hotspot, the value is only loaded into a register once, and that's the end of it. You can verify this hypothesis by using the -XX:+PrintCompilation command line flag.

like image 93
biziclop Avatar answered Nov 15 '22 17:11

biziclop