Ravi Krishna P
Ravi Krishna P

Reputation: 147

Java concurrency with respect to syncronization block in loop

I have a simple code that passes the same object to a different class which will be used for locking (not ideal but assume this as a possibility). What should be the output?

  1. MorningThread just prints "Good Morning" in the synchronized block inside a for a loop.
  2. EveningThread just prints "Good Evening" in the synchronized block inside a for a loop.
    private Object lock;

    MorningThread(Object lock) {
        this.lock= lock;
    }

    public void run() {
        for (int i = 0; i < 2; i++) {
            synchronized(lock) {
                System.out.println("Good Morning");
            }
        }
    }
}

class EveningThread extends Thread {
    private Object lock;

    EveningThread(Object lock) {
        this.lock= lock;
    }

    public void run() {
        for (int i = 0; i < 2; i++) {
            synchronized(lock) {
                System.out.println("Good Evening");
            }
        }
    }
}

public class HelloWorld {
    public static void main(String[] args) throws Exception {

        Object lock = new Object();

        new MorningThread(lock ).start();
        new EveningThread(lock ).start();
    }
}

As per my understanding, it can be various combinations (practically speaking) but theoretically speaking it should be below because Morning will execute first and by this time Evening will be waiting and Evening executes by which time Morning is waiting and henceforth.

  1. Good Morning
  2. Good Evening
  3. Good Morning
  4. Good Evening

But strangely it is always (when I ran on a windows machine)

  1. Good Morning
  2. Good Morning
  3. Good Evening
  4. Good Evening

So I started to think that there could be some optimization with JVM where it recognizes the continuation flow in and does not give the lock to Evening till Morning has finished. Could that be true? (I could not find the right Java doc that calls this out)

Upvotes: 1

Views: 65

Answers (2)

Gray
Gray

Reputation: 116878

As per my understanding, it can be various combinations [of output] (practically speaking) ...

This part of your sentence is right.

... but theoretically speaking it should be below because Morning will execute first and by this time Evening will be waiting and Evening executes by which time Morning is waiting and henceforth.

This statement is incorrect. There should be no expectation of thread ordering theoretical or otherwise. Starting a thread is expensive (relatively speaking) so after the 'good morning' thread is started, it will run, lock, print, lock, print and finish before the 'good evening' thread is ever started.

Java threads are designed to run asynchronously and the order of their operation is very hard or impossible to predict in most circumstances. Running in this manner is how they get their speed. If you increased your loop counters to (let's say) 20000 then you should see some intermixing of output but again it is most likely going to be blocks of morning and then blocks of evening messages and not M E M E ME ... Anyone that is expecting a particular ordered output from a threaded program probably should not be using threads or should collect the output and then sort it at the end.

Couple other comments:

  • The lock object should be private final Object lock ....
  • Injecting a lock object into the constructor of a threaded object is a good pattern instead of being "not ideal".
  • The System.out.println(...) is synchronized as well. Any use of System.out can change how a threaded program runs and so should be used carefully.

Upvotes: 2

Emmef
Emmef

Reputation: 522

The JVM, and even its just-in-time (JIT) compiler, cannot make optimisations that are related to the flow in different threads in an effective way.

Java threads are generally strongly related to native operating system (OS) threads. The OS starts the threads. This means that one thread could be finished before the other is even started! The OS also pauses and resumes threads. This can happen pre-emptively at almost any CPU instruction. As both the JVM or your JIT-compiled code are just programs, coded in CPU instructions, they generally won't even realise that they are paused or resumed.

Therefore, the JIT compiler cannot build meaningful statistics about flows in different threads, let alone optimise them.

Upvotes: 0

Related Questions