How run() method Works internally in Java

In this post, we will see how run() method works internally in Java. Sometime in the interview, you may encounter this question. Also, we will see what will happen when we call run() method explicitly.

The run() method invoked by JVM while calling start() method(we will see how in the upcoming example). let’s see an example.

package multithreading;

public class ThreadExample extends Thread {

	@Override
	public void run() {

		System.out.println("Thread created with name: - " + Thread.currentThread().getName());
	}

	public static void main(String[] args) {
		ThreadExample threadExample = new ThreadExample();
		threadExample.start();

	}

}

Output is – Thread created with name: – Thread-0

In the above example, we didn’t come call run() method explicitly. As soon as we do threadExample.start() JVM will internally call run() method. Till now no confusion. Now the question comes how JVM calls run() method internally? Is there any logic defined inside start() method?

To answer above questions let’s dive into start() method. Internally start() method is implemented like below.

public synchronized void start() {
       
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                
            }
        }
    }

This is all code we have in start() method. You may be expecting run() method inside this, but there is no run method and also we have only one method i.e start0() which may further call run() method. Yes, start0() method is responsible to call run method. Let’s have a look into start0() method.

Unfortunately, there is no implementation logic is there in the thread class, only we have a declaration something like below.

private native void start0();

Again it’s a little confusing, isn’t it? what magic is going on here?  Let’s see.

First, we need to understand what does it mean of the native method. In Thread class couple of methods are native like yield(), start0(),isActive() etc. It means these method implementation has been given in some different language like c/c++. Similarly, for start0() method, we have the only declaration implementation has been written in different language i.e in C and yes we have a separate library for that. Here JNI(Java Native Interface) comes in the picture which helps JVM to calls native library which has been written in C or C++ languages.

Let’s see how does it look.

#include "jni.h"
	#include "jvm.h"

	#include "java_lang_Thread.h"

	#define THD "Ljava/lang/Thread;"
	#define OBJ "Ljava/lang/Object;"
	#define STE "Ljava/lang/StackTraceElement;"

	#define ARRAY_LENGTH(a) (sizeof(a)/sizeof(a[0]))

	static JNINativeMethod methods[] = {
	    {"start0",           "()V",        (void *)&JVM_StartThread},
	    {"stop0",            "(" OBJ ")V", (void *)&JVM_StopThread},
	    {"isAlive",          "()Z",        (void *)&JVM_IsThreadAlive},
	    {"suspend0",         "()V",        (void *)&JVM_SuspendThread},
	    {"resume0",          "()V",        (void *)&JVM_ResumeThread},
	    {"setPriority0",     "(I)V",       (void *)&JVM_SetThreadPriority},
	    {"yield",            "()V",        (void *)&JVM_Yield},
	    {"sleep",            "(J)V",       (void *)&JVM_Sleep},
	    {"currentThread",    "()" THD,     (void *)&JVM_CurrentThread},
	    {"countStackFrames", "()I",        (void *)&JVM_CountStackFrames},
	    {"interrupt0",       "()V",        (void *)&JVM_Interrupt},
	    {"isInterrupted",    "(Z)Z",       (void *)&JVM_IsInterrupted},
	    {"holdsLock",        "(" OBJ ")Z", (void *)&JVM_HoldsLock},
	    {"getThreads",        "()[" THD,   (void *)&JVM_GetAllThreads},
	    {"dumpThreads",      "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
	};

Did you observe line number 13, we have start0() and some other methods here which have defined in Thread class.

Now come to another point what will happen when we will call run() method explicitly? In this case, the thread will not be created and this will be treated as a normal method.

package multithreading;

public class ThreadExample extends Thread {

	@Override
	public void run() {

		System.out.println("Thread created with name: - " + Thread.currentThread().getName());
	}

	public static void main(String[] args) {
		ThreadExample threadExample = new ThreadExample();
		threadExample.run();

	}

}

Output is – Thread created with name: – main

In the above output, the thread name is main. By default even if we write hello world program we have a thread with the name main thread.

That’s all about How run() method works internally in Java.

Summary – The run() method will get invoked by JVM while calling start() method. In start() further start0() method will get called which further calls run() method using JNI libraries. Here JNI means (java+C or C++).

You may like.

run() method docs.