0

I have tested something about multithreading. I find the snippet lasts much more than 3 seconds, and is not printing the last System.out.println("program end");. Why?

public class hello {
    static Boolean flag = false;
    public static void main(String args[]) throws InterruptedException {
        MyThread t = new MyThread();
        t.start();
        Thread.sleep(3000);
        hello.flag = true;
    }
    static class MyThread extends Thread {

        public void run(){
            System.out.println("Thread start");
            while(true){
                if(hello.flag){
                    break;
                }
            }
            System.out.println("Thread end");//why not print this statement? but run in debug mode, it will print this statement correctly
        }

    }

}

program run result as follow:

khelwood
  • 55,782
  • 14
  • 81
  • 108
sucan
  • 3
  • 1
  • Because it's not thread safe. – grape_mao Jul 26 '18 at 08:57
  • 2
    Threads may cache their own copies of variables. That's why we have `volatile`, `synchronized` etc. – khelwood Jul 26 '18 at 08:58
  • 2
    @grape_mao - That's hardly a useful comment. Fleshed out, it could be an answer, but just "Because it's not thread safe." isn't constructive. – T.J. Crowder Jul 26 '18 at 08:59
  • thanks for your reply, but when I put a system.out.println("something") before "if (hello.flag) statement". the program will end in 3 seconds. – sucan Jul 26 '18 at 09:00
  • @T.J.Crowder you are right, I was hoping he could go and find out why it's not thread safe, and that' why I only left a comment. – grape_mao Jul 26 '18 at 09:02
  • While your program has no reason to work as expected when not thread-safe, it is however not guaranteed that it won't work as expected. Sometimes chance will just do stuff the way you wanted them. (Notably because System.out.println() triggers some synchronization mechanisms.) It's just you can't rely on it. – kumesana Jul 26 '18 at 09:04
  • @sucan That's because `System.out.println` will synchronize the variables. – khelwood Jul 26 '18 at 09:05
  • Also see this: https://stackoverflow.com/questions/25425130/loop-doesnt-see-changed-value-without-a-print-statement. – Maroun Jul 26 '18 at 09:07
  • You should only use `Boolean` because you have an API which requires it or you expect it to be `null`. – Peter Lawrey Jul 26 '18 at 09:10
  • The JIT is free to inline non volatile variables so it is never read esp if a thread doesn't write to it. This means it might never see the change. – Peter Lawrey Jul 26 '18 at 09:13

2 Answers2

0

Different threads use different memory space caches each.

It means the first thread has the variable in its own cache, and the other thread has the variable in its own cache. Thus each thread seeing the same variable in different states.

In the absence of appropriate synchronization mechanisms between the two thread, they have no reason to try and reconcile the difference in their caches. It would severely deter performances to do that without being instructed to.

One very easy synchronization mechanism you could have here, would be to make variable flag, volatile. That will make the threads synchronize their caches on this variable on each read/write.

kumesana
  • 2,495
  • 1
  • 9
  • 10
  • Thanks for your clarification. but one thing still confuses me is when I add a print statement in the while loop. even if i do not add volatile keyword on the flag. the program will end in 3 seconds. in this situation, how can the two thread synchronize there each read/write? why? – sucan Jul 26 '18 at 09:15
  • Just because your program is not guaranteed to work if you don't take the appropriate precautions, it is not guaranteed to not work either. There is little sense wondering why it may sometimes work without having done things correctly. What matters is that it is likely to not work unless you do things correctly. Still, the reason why a print statement changes things in some way, is most likely because print statements thread-safe, so they provoke synchronization mechanisms. This does not at all ensure that "everything will be fixed between threads". But it may fix one case as side effect. – kumesana Jul 26 '18 at 09:21
  • ok, thanks. I know to do the sync is the best practice.;) – sucan Jul 26 '18 at 09:29
0

Use volatile with flag to make thread read actual value of flag not from its local cache..

public class hello {
   //make flag volatile
   volatile static Boolean flag = false;
    public static void main(String args[]) throws InterruptedException {
        MyThread t = new MyThread();
        t.start();
        Thread.sleep(3000);
        hello.flag = true;
    }
    static class MyThread extends Thread {

        public void run(){
            System.out.println("Thread start");
            while(true){
                if(hello.flag){
                    break;
                }
            }
            System.out.println("Thread end");//why not print this statement? but run in debug mode, it will print this statement correctly
        }

    }

}

Please go through below link to see how it works..

https://docs.oracle.com/cd/E19683-01/806-5222/codingpractices-1/index.html

DhaRmvEEr siNgh
  • 1,918
  • 2
  • 13
  • 17
  • thank you, it works after add the volatile keyword. and the reference URL you provided is worthy for understanding how and when to use volatile. – sucan Jul 26 '18 at 09:39