3

Trying to wrap my head around this code. When I run this - the output will be Roger. Isn't msg a static variable and at a class level thus should print Moore?

EDIT : I've allowed a sleep too allow the child thread to run its course. It also prints printing... Still No Change

public class Test2 { private static String msg = "Roger"; static { new Thread(new Runnable() { public void run() { System.out.println("printing.."); msg += "Moore"; } }).start(); } static { try { Thread.sleep(1000); } catch (InterruptedException e) { } } public static void main(String argv[]) { System.out.println(msg); } } 
2
  • 1
    It looks like a simple race condition. I wonder if, in main(), you sleep for a second, will it then output "Moore" instead of "Roger"? Commented Sep 30, 2013 at 18:50
  • yes - that was it..adding a Thread.sleep for a longer duration did print the relevant output! Thanks for the answer! Please add yours to answer so I can "Answered" it..or I can answer papmplhet - both are right... Commented Sep 30, 2013 at 18:56

4 Answers 4

5

Trying to wrap my head around this code. When I run this - the output will be Roger. Isn't msg a static variable and at a class level thus should print Moore?

As others have pointed out, this is a race condition but it's more complicated then this simple answer.

EDIT : I've allowed a sleep too allow the child thread to run its course. It also prints printing... Still No Change

When a class is initialized, the static code is executed in the thread that accesses the class first – in this case the main thread. All other threads have to wait for this initialization to complete before they can access the class. This means that the background thread actually stops and waits for the class initialization to complete before it can execute msg += "Moore";. Then it is a race to see whether the msg is assigned to "Roger" and the background thread can append to it before main prints it. Even with the msg field being volatile, the race still exists. You can get a glimpse into the complexities of the process from the JLS section 12.4.2 on Detailed Initialization Procedure.

So what is happening is approximately:

  1. The main thread initializes the Test2 class.
  2. The msg is initialized first because it comes before the static blocks.
  3. First static block is executed which forks the background thread.
  4. Second static block is executed which does the sleep() blocking the initializing thread.
  5. Background thread starts to run (could be before the previous step). It goes to update msg but the class is locked since the main thread is sleeping and hasn't completed with the class initialization. The background thread has to wait.
  6. The main thread wakes up and finishes the initialization.
  7. This releases the block on the class which allows the background thread to continue.
  8. At the same time as the previous step, main is called and it is a race condition to see if the msg can be updated before it is printed out.

In general, forking background threads in static methods like this is extremely frowned upon. Putting a sleep in a static block is obviously not recommended as well.

Sign up to request clarification or add additional context in comments.

8 Comments

+1: Interesting. Is there a chapter in the JSL about what is happening?
Found it @MartijnCourteaux. Section 12.4.2 on Detailed Initialization Procedure. docs.oracle.com/javase/specs/jls/se7/html/…
I was reading that section for a couple of minutes, but I think this is more about WHO (= which thread) is initializing the class, and not really preventing access to class members. However, I'll reread.
No, you are right @MartijnCourteaux. I've edited my answer to make that more evident. I wasn't trying to say there was a separate thread doing it.
In fact, if you interpret all the rules about static class initializers strictly, §12.4.2 is the perfect explanation to this behavior. Because the second thread tries to initialize the class as well, because when it tries to write to msg, it sees that the class loading isn't done yet, but this is not allowed as described in the that section. It states: If the Class object for C indicates that initialization is in progress for C by some other thread, then release LC and block the current thread until informed that the in-progress initialization has completed, at which time repeat this step.
|
2

The main method will not be called till all the static initializers in your class are done. So it will always wait till the static inits are done. Even if there is a sleep in it.

Additionaly static initialization is thread safe, so your forked thread cannot access the variable, till the static init blocks are done.

Comments

2

It's a race condition. There's no guarantee when the Runnable will have executed.

EDIT: This answers responds to the original posted question, in which no delay was present in the static initializer. This was leading to a simple race condition between the main thread reading the static member and the spawned thread updating it.

7 Comments

This doesn't seem to be a race condition to me. Sleeping one second should give the other thread enough time to finish. Check this example: ideone.com/DVMZ0f
@MartijnCourteaux, OP has confirmed that delaying the main thread changed the output.
Sorry, but I'm downvoting. This behavior is caused by the fact that Java protects other threads from accessing variables of uninitialized classes.
@MartijnCourteaux, If that's the case, why would the the behaviour change after a delay was introduced in the main thread. Doesn't that speak to a timing condition?
If you delay the thread in the main method, the static initializer is finished. This way, the second thread can do its work. In his code example, he is sleeping within the static initializer. This means that the class initialization isn't finished yet, while the other thread is trying (in fact waiting for) to change the static variable msg.
|
0

Rather than wait a little bit, and hope the other thread runs, you can guarentee it with some synchronization:

public class Test { private static String msg = "Roger"; private static volatile boolean done = false; private static final Object lock = new Object(); static { new Thread(new Runnable() { public void run() { synchronized(lock) { lock.notify(); System.out.println("printing.."); msg += "Moore"; done=true; } } }).start(); } public static void main(String argv[]) { synchronized(lock) { while(!done) { try { lock.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } System.out.println(msg); } } 

If the main thread aquires the lock first, then it will msg.wait. It will not continue until notify is called(actually, it continues when the synchronized block containing notify finishes). If the new thread aquires the lock first, then the main thread will have to wait at the start of it's synchronization block. Once it gets in, done will be true. It will not wait, and go straight through.

2 Comments

This won't work for a couple of reasons. done should be volatile if it is updated in multiple threads. But the real problem is that you are trying to synchronize on an object whose reference is changing. That never works.
Lock objects should always be final if possible Cruncher. Otherwise this looks good.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.