Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java threads - start() and run() - lost line in console

Why is another "TaskImpl run()" in Scenario 1 below missing ?

Scenario 1: if the line marked with 1. is before the line marked with 2. this is shown in the console:

TaskImpl run()
ThreadImpl run()
ThreadImpl run()
Finished

Scenario 2: If the line marked with 2. is before the line marked with 1. this is shown in the console:

TaskImpl run()
TaskImpl run()
ThreadImpl run()
ThreadImpl run()
Finished

My code :

public class ThreadTest {
    public static void main(String[] args) 
    {
        Thread t1 = new ThreadImpl();
        Thread t2 = new Thread(new TaskImpl());

        t1.start();
        t2.start();

        t1.run();   // 1.
        t2.run();   // 2. 

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("Finished");
    }
}

class ThreadImpl extends Thread {
    @Override
    public void run() {
        try {
            Thread.sleep(2000);
            System.out.println("ThreadImpl run()");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

class TaskImpl implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            System.out.println("TaskImpl run()");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
like image 980
Slipknot Avatar asked Aug 15 '26 09:08

Slipknot


1 Answers

As you can see in the implementation of Thread#run the run method of the target (the Thread implementation/subclass which should be run in a thread) will only be called if target is not null:

@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

Now another info: If a thread has done its job it calls the private exit method which sets target to null (*):

private void exit() {
    // [omitted code lines]

    /* Aggressively null out all reference fields: see bug 4006245 */
    target = null;

    // [omitted code lines]
}

So if you called start on a Thread and it has done its job, then you can't call run again ... well you can, but I won't do much.

Now let's look at your first version of your code:

t1.start(); -> starts a thread; takes at least 2 seconds to finish
t2.start(); -> starts a thread; takes at least 1 second to finish

t1.run(); -> blocks main thread; takes at least 2 seconds to finish
t2.run(); -> called after `t1.run()` finished;

So as you can see, t2.run() will run at least 2 seconds after t2.start() because t1.run() blocks the method call. So t2 (from t2.start()) already finished and set target to null so t2.run() fails the target != null check and does "nothing".

Now the second version:

t1.start(); -> starts a thread; takes at least 2 seconds to finish
t2.start(); -> starts a thread; takes at least 1 second to finish

t2.run(); -> blocks main thread; takes at least 1 seconds to finish
t1.run(); -> called after `t2.run()` finished;

Here t2.run() can run before t2 from t2.start() could have finished his "sleep" so target is still set correctly.
And t1.run() also has no problem, because it directly calls the overriden run method of ThreadImpl, so it doesn't need to pass the check. And it wouldn't have a problem to pass it anyway, because t2.run() sleeps for 1 second, so t1 from t1.start() still sleeps for another second and his target is also still set.
That is why you get the "full" output.

I hope my explanation is clear and you understand the cause for the different output :).

(*) Please mind that this behavior my vary in other Java Environments. But the Oracles version and OpenJDK are doing that.

like image 139
Tom Avatar answered Aug 18 '26 00:08

Tom



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!