3

Below is the code. The output is 1000, b=1000. I don't understand why m2() runs first every time.

public class TT implements Runnable{
    int b = 100;

    public synchronized void m1() throws Exception {
        b = 1000;
        Thread.sleep(100);
        System.out.println("b =" + b);
    }

    public synchronized void m2() throws Exception {
        Thread.sleep(2000);
        b = 2000;
    }

    public void run() {
        try {
            m1();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        TT tt = new TT();
        Thread t = new Thread(tt);
        t.start();// m1() starts

        tt.m2();// m2() starts

        System.out.println(tt.b);
    }

}
Platinum Azure
  • 45,269
  • 12
  • 110
  • 134
Bill
  • 33
  • 2
  • 3
    Why shouldn't it? There's nothing in the specs which imply that the thread must immediately run and that the current thread cannot resume until the new thread is done. You have a race condition. – Platinum Azure Nov 17 '14 at 04:00
  • 3
    What do you mean by `m2() starts`? It doesn't start -- you call the `m2` function and it runs to completion. – David Schwartz Nov 17 '14 at 04:01
  • Don't sleep in `m1` or `m2` and I get 2000, 1000. – Elliott Frisch Nov 17 '14 at 04:04
  • You cannot guarantee that m2() runs first at all the time. It is a race condition some time m2() will be called first and sometime m1 (run method) will call first. Try to `debug using IDE` and just have a break point in two suspicious point and give step run you can find more. – Jaya Ananthram Nov 17 '14 at 04:05

3 Answers3

3

The real reason1 the synchronized keyword.

The m2() method acquires the lock on the TT instance, and then sleeps. Meanwhile, the thread start takes effect and the run() method attempts to call m1(). But m1() has to wait until the call to m2() completes and releases the lock.

Now if the new thread is started and manages to call m1() before the existing thread calls m2(), then you will see the events in the other order. This is theoretically possible ... but unlikely on typical Java platforms. (Typically, the parent thread is not descheduled when it starts a child thread.)

What happens after m2() returns will determine the output you see. Since you accessing tt.b directly (without any synchronization), the result is not predictable from a theoretical perspective.


Basically, this just illustrates that sleep is the wrong way to do thread scheduling. If you want one event on one thread to happen before another event on a different, the threads need to coordinate explicitly. Relying on sleep and serendipitous timing effects (like thread.start() typically taking a long time) will give you unreliable behavior.

1 - Obviously, it is a combination of things. But the real reason with the sleep hackery is not working as expected is the synchronized keywords.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
1

Starting a thread takes a while. An existing thread that already is executing has a big advantage, it already exists and only has to acquire a lock, the other depends on creating a new thread before it can acquire a lock. A thread only has to acquire a lock to win the race, shutting out the other, the thread that doesn't have to get created from scratch will most likely get there first.

It is still a race condition and you shouldn't write code that depends on who gets there first.

Community
  • 1
  • 1
Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
1

If you want tt.m2() to run after t thread finished it's job. Add t.join() after t.start(). This makes the main Thread to run after t has finished its job.

saikumarm
  • 1,565
  • 1
  • 15
  • 30