We all know that a Thread corresponds directly to a Thread object, and when we first started learning about threads we also knew that threads were started using the start() method, not the run() method.
So why is that?
If you are familiar with Thread’s code, you should know that some native methods are registered when the class loads
public
class Thread implements Runnable {
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives(a);
static {
registerNatives();
}
}
Copy the code
As soon as I saw Native I remembered JNI,registerNatives() is actually a Java method and C/C++ function corresponding. These native methods are registered at first load. There are many native methods in Thread. If you are interested, you can go to see them.
For naming JNI methods, we can test this by declaring a native method in Java, Then you compile the source file using Javac (for example, javac main.java) and then generate the header file using Javah (javah main). Open this header file and you will know what the method name is
We can see the implementation of the registerNatives method by searching Java_java_lang_Thread_registerNatives in the JVM source code
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 "The [[" STE, (void *)&JVM_DumpThreads},
{"setNativeName"."(" STR ")V", (void *)&JVM_SetNativeThreadName},
};
JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
Copy the code
You can see that in the registerNatives function, many native methods such as the start0() method are registered.
All calls to JNI functions use the env pointer, which is the first argument to each local method. The env pointer is a pointer to the function pointer table. We can in docs.oracle.com/javase/8/do… To find the JNI API
In the thread.start () method, the Thread is actually started by calling start0().
public synchronized void start(a) {
if(threadStatus ! =0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
// The start0() native method is called to start the thread
start0();
started = true;
} finally {
try {
if(! started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0(a);
Copy the code
The JNINativeMethod data structure is defined as follows:
typedef struct {
char *name;
char *signature;
void *fnPtr;
}
Copy the code
So the local function corresponding to the start0() method is JVM_StartThread
{"start0"."()V", (void *)&JVM_StartThread}
Copy the code
Let’s look at the JVM_StartThread method implementation next
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
JavaThread *native_thread = NULL;
bool throw_illegal_thread_state = false;
{
// Ensure that the C++ Thread and OSThread structures aren't freed before
// we operate.
MutexLocker mu(Threads_lock);
// Starting with JDK5, use java.lang.Thread threadStatus to prevent restarting an already started Thread
if(java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) ! =NULL) {
throw_illegal_thread_state = true;
} else {
jlong size =
java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
NOT_LP64(if (size > SIZE_MAX) size = SIZE_MAX;)
size_t sz = size > 0 ? (size_t) size : 0;
// Create a Java thread
native_thread = new JavaThread(&thread_entry, sz);
if(native_thread->osthread() ! =NULL) {
native_thread->prepare(jthread);
}
}
}
if (throw_illegal_thread_state) {
THROW(vmSymbols::java_lang_IllegalThreadStateException());
}
// Some code is omitted
// Set the thread state to Runnable, indicating that it can be run
Thread::start(native_thread);
JVM_END
Copy the code
The code above does three main things
-
Judge whether the current thread state legal and illegal sell IllegalThreadStateException
-
Creating a Java thread (what we need to focus on)
-
Set the thread state to Runnable
If the interviewer will ask you later two call start () method, you can raise IllegalThreadStateException bold and firm reply.
The OS ::create_thread method is actually called in the JavaThread constructor
bool os::create_thread(Thread* thread, ThreadType thr_type,
size_t req_stack_size) {
OSThread* osthread = new OSThread(NULL.NULL);
if (osthread == NULL) {
return false;
}
osthread->set_thread_type(thr_type);
osthread->set_state(ALLOCATED);
thread->set_osthread(osthread);
// init thread attributes
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
size_t stack_size = os::Posix::get_initial_stack_size(thr_type, req_stack_size);
int status = pthread_attr_setstacksize(&attr, stack_size);
ThreadState state;
{
pthread_t tid;
// Create a thread
int ret = pthread_create(&tid, &attr, (void* (*) (void*)) thread_native_entry, thread);
// omit other code...
}
return true;
}
Copy the code
The pthread_create function creates a thread. Its third argument is the starting address of the function that the thread runs, and its fourth argument is the argument that runs the function
Threads are defined in the IEEE standard 1003.1c, which defines a thread package called PThreads. Most UNIX systems support this standard.
Our thread_native_entry actually passes in JavaThread, so we end up calling JavaThread::run()(in thread.cpp).
void JavaThread::run() {
.
thread_main_inner();
.
}
void JavaThread::thread_main_inner() {
.
this->entry_point()(this.this);
.
}
Copy the code
The return value of entry_point in the thread_main_inner function is actually the first thread_entry argument we passed in when we created JavaThread. The thread_entry pointer points to the following function:
static void thread_entry(JavaThread* thread, TRAPS) {
HandleMark hm(THREAD);
Handle obj(THREAD, thread->threadObj());
JavaValue result(T_VOID);
JavaCalls::call_virtual(&result,
obj,
SystemDictionary::Thread_klass(),
// run method name run
vmSymbols::run_method_name(),
// Method signature ()V
vmSymbols::void_method_signature(),
THREAD);
}
Copy the code
So we finally call the run method through JavaCalls.
conclusion
The new Thread simply creates a normal Java object, and a real Thread is created only after the start() method is called. Within the JVM, the run() method is called after the Thread is created to execute the corresponding business logic.
Thread resources are an important resource because Java threads are ultimately tied to operating system threads, and threads are typically used as thread pools for reuse purposes.
Focus on me not getting lost
I am me, different fireworks