Hi~ O ( ̄▽ ̄)ブ ブ, good morning, my friends
Get on the adventurer 4ye boat and explore hotspot source continent ~ 😝
This article introduces the mystery of Thread start
Thread Start source code revealed
public synchronized void start(a) {
/** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */
if(threadStatus ! =0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
// Look here ~
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 */}}}Copy the code
From the above code, we can see that threadStatus checks to see if it is 0 and raises an exception if it is not.
ThreadStatus defaults to 0
-
When will that change?
-
What are the values of this threadStatus?
Hey hey with a little doubt to continue to see pa ~ 😝
start0
The change in state must be accompanied by the start of the thread, so we go straight to the start0 method below
private native void start0(a);
Copy the code
It can be found that it is a native function and we can directly
The OpenJDK JDK source code global search, you can find it
Path: the JDK \ SRC \ share \ native \ Java \ lang \ Thread c
JNINativeMethod
This JNINativeMethod is a structure
Path: the JDK \ SRC \ share \ javavm \ export \ jni h
/* * used in RegisterNatives to describe native method name, signature, * and function pointer. */
typedef struct {
char *name;
char *signature;
void *fnPtr;
} JNINativeMethod;
Copy the code
JVM_StartThread
Along the way, we found the JVM_StartThread method
path: jdk\src\share\javavm\export\jvm.h
/* * java.lang.Thread */
JNIEXPORT void JNICALL
JVM_StartThread(JNIEnv *env, jobject thread);
Copy the code
Here JNIEXPORT and JNICALL are JNI keywords, indicating that this function is to be called by JNI
Seeing that it is a JVM starting file, let’s go to the following hotspot source and look at 😋
Found here is the use of 👉
path: hotspot\src\share\vm\prims\jvm.cpp
Code:
JVM_ENTRY(void.JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
JavaThread *native_thread = NULL;
// We cannot hold the Threads_lock when we throw an exception,
// due to rank ordering issues. Example: we might need to grab the
// Heap_lock while we construct the exception.
bool throw_illegal_thread_state = false;
// We must release the Threads_lock before we can post a jvmti event
// in Thread::start.
{
// Ensure that the C++ Thread and OSThread structures aren't freed before
// we operate.
MutexLocker mu(Threads_lock);
// Since JDK 5 the java.lang.Thread threadStatus is used to prevent
// re-starting an already started thread, so we should usually find
// that the JavaThread is null. However for a JNI attached thread
// there is a small window between the Thread object being created
// (with its JavaThread set) and the update to its threadStatus, so we
// have to check for this
if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) ! =NULL) {
throw_illegal_thread_state = true;
} else {
// We could also check the stillborn flag to see if this thread was already stopped, but
// for historical reasons we let the thread detect that itself when it starts running
jlong size =
java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
// Allocate the C++ Thread structure and create the native thread. The
// stack size retrieved from java is signed, but the constructor takes
// size_t (an unsigned type), so avoid passing negative values which would
// result in really large stacks.
size_t sz = size > 0 ? (size_t) size : 0;
native_thread = new JavaThread(&thread_entry, sz);
// At this point it may be possible that no osthread was created for the
// JavaThread due to lack of memory. Check for this situation and throw
// an exception if necessary. Eventually we may want to change this so
// that we only grab the lock if the thread was created successfully -
// then we can also do this check and throw the exception in the
// JavaThread constructor.
if (native_thread->osthread() != NULL) {
// Note: the current thread is not being used within "prepare".
native_thread->prepare(jthread); }}}if (throw_illegal_thread_state) {
THROW(vmSymbols::java_lang_IllegalThreadStateException());
}
assert(native_thread ! =NULL."Starting null thread?");
if (native_thread->osthread() = =NULL) {
// No one should hold a reference to the 'native_thread'.
delete native_thread;
if (JvmtiExport::should_post_resource_exhausted()) {
JvmtiExport::post_resource_exhausted(
JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
"unable to create new native thread");
}
THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
"unable to create new native thread");
}
// Look here ~
Thread::start(native_thread);
JVM_END
Copy the code
Here JVM_ENTRY and JVM_END are two macro definitions that define the start and end of the function body. 😄
There are too many details here to chew 😅
Talk about a few ~ cough cough also look at the author’s annotation translation 🐷
Such as:
Create a thread
native_thread = new JavaThread(&thread_entry, sz);
Copy the code
Go to the constructor
JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
Thread(a)#if INCLUDE_ALL_GCS
, _satb_mark_queue(&_satb_mark_queue_set),
_dirty_card_queue(&_dirty_card_queue_set)
#endif // INCLUDE_ALL_GCS
{
if (TraceThreadEvents) {
tty->print_cr("creating thread %p".this);
}
initialize(a); _jni_attach_state = _not_attaching_via_jni;set_entry_point(entry_point);
// Create the native thread itself.
// %note runtime_23
os::ThreadType thr_type = os::java_thread;
thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread :
os::java_thread;
// Look here ~
os::create_thread(this, thr_type, stack_sz);
_safepoint_visible = false;
}
Copy the code
Creating a kernel thread
Found here is to create a kernel thread ~ 👍
os::create_thread(this, thr_type, stack_sz);
Copy the code
Follow the train of thought continue to explore ~
Linux scenario
There is the following code 🐂
path: hotspot\src\os\linux\vm\os_linux.cpp
Finally, a function that looks vaguely familiar
int ret = pthread_create(&tid, &attr, (void* (*) (void*)) java_start, thread);
Copy the code
The bottom content is really crazy ~ endless.. Ha, ha, ha
Here, cut off the pointer function java_start, if you are interested, keep going! 😄
Starting a thread
Thread::start(native_thread); 支那
Thread::start
Come down here ~
path: hotspot\src\share\vm\runtime\thread.cpp
The function of the first red box:
Before starting the thread, initialize the thread state to RUNNABLE.
Cannot be set after the thread is started because we do not know the correct thread state, which may be in MONITOR_WAIT or in sleep or other states.
The second red box starts the kernel thread!
// The INITIALIZED state is distinguished from the SUSPENDED state because the // conditions in which a thread is first started are different from those in which // a suspension is resumed. These differences make it hard for us to apply the // tougher checks when starting threads that we want to do when resuming them. // However, when start_thread is called as a result of Thread.start, on a Java // thread, the operation is synchronized on the Java Thread object. So there // cannot be a race to start the thread and hence for the thread to exit while // we are working on it. Non-Java threads that start Java threads either have // to do so in a context in which races are impossible, or should do appropriate // locking. void os::start_thread(Thread* thread) { // guard suspend/resume MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag); OSThread* osthread = thread->osthread(); osthread->set_state(RUNNABLE); pd_start_thread(thread); }Copy the code
Pd_start_thread = pd_start_thread = pd_start_thread = pd_start_thread
The results are shown in figure ~
Finally, record the global function notify 😝
bool Monitor::notify(a) {
assert (_owner == Thread::current(), "invariant");assert (ILocked(), "invariant");if (_WaitSet == NULL) return true ;
NotifyCount ++ ;
// Transfer one thread from the WaitSet to the EntryList or cxq.
// Currently we just unlink the head of the WaitSet and prepend to the cxq.
// And of course we could just unlink it and unpark it, too, but
// in that case it'd likely impale itself on the reentry.
Thread::muxAcquire (_WaitLock, "notify:WaitLock"); ParkEvent * nfy = _WaitSet ;if(nfy ! =NULL) { // DCL idiom
_WaitSet = nfy->ListNext ;
assert (nfy->Notified == 0."invariant");// push nfy onto the cxq
for (;;) {
const intptr_t v = _LockWord.FullWord ;
assert ((v & 0xFF) == _LBIT, "invariant"); nfy->ListNext = (ParkEvent *)(v & ~_LBIT);if (CASPTR (&_LockWord, v, UNS(nfy)|_LBIT) == v) break;
// interference - _LockWord changed -- just retry
}
// Note that setting Notified before pushing nfy onto the cxq is
// also legal and safe, but the safety properties are much more
// subtle, so for the sake of code stewardship ...
OrderAccess::fence() ;
nfy->Notified = 1;
}
Thread::muxRelease (_WaitLock) ;
if(nfy ! =NULL && (NativeMonitorFlags & 16)) {
// Experimental code ... light up the wakee in the hope that this thread (the owner)
// will drop the lock just about the time the wakee comes ONPROC.
nfy->unpark() ;
}
assert (ILocked(), "invariant");return true ;
}
Copy the code
Surprise to see this comment in this code, which mentions WaitSet, EntryList, and CXQ
This is an implicit lock 🔒 [[Synchronized’s internal implementation]] or [[Monitor]] 👍
WaitSet
Is a wait queue for threads that enter the wait statecxq
Is a competing queue, where all threads requesting the lock 🔒 will arrive firstEntryList
storecxq
Threads that qualify as candidate resources to compete for locks
// Transfer one thread from the WaitSet to the EntryList or cxq.
// Currently we just unlink the head of the WaitSet and prepend to the cxq.
// And of course we could just unlink it and unpark it, too, but
// in that case it'd likely impale itself on the reentry.
Thread::muxAcquire (_WaitLock, "notify:WaitLock");Copy the code
Hey hey back into the lock part to share 😝
threadStatus
Back at the top, there’s still one problem left to solve
thisthreadStatus
What are the values?
Here we can get 👇 from the jvm.h source code
/* * Java thread state support */
enum {
JAVA_THREAD_STATE_NEW = 0,
JAVA_THREAD_STATE_RUNNABLE = 1,
JAVA_THREAD_STATE_BLOCKED = 2,
JAVA_THREAD_STATE_WAITING = 3,
JAVA_THREAD_STATE_TIMED_WAITING = 4,
JAVA_THREAD_STATE_TERMINATED = 5,
JAVA_THREAD_STATE_COUNT = 6
};
Copy the code
conclusion
Thread Start: Thread Start: Thread Start: Thread Start: Thread Start After being beaten by Hotspot source Boss, the 4ye adventuter can only run away with a little experience ε=ε=ε=( ̄▽ ̄) hahaha 🐷)
Harvest:
Learned a little C and C++ syntax hahaha
Knowing the nature of thread creation, I have the following insights for Linux scenarios
Discover that for every Java thread, there is a kernel thread;
Can better understand the existence of user mode and kernel mode;
Learn that when a thread is started, its state changes before it is started;
The actual action of starting the kernel thread is to call the Monitor::notify method to wake it up 👍
At the same time, a trace of Monitor mechanism is detected in this method.
Saw mutex everywhere;
It’s better to feel that Java is running everywhere for a single compilation haha, the JVM has already wrapped the corresponding OS
Source code reading experience +1 hahaha
Open a new continent: later you can also try to compile, do your own JVM 😝