Here is a basic code example
Main Main thread
T1 is the child thread
public static void main(String[] args) throws InterruptedException {
// Initialization state
Thread t1 = new Thread(() -> {});
// Runnable/running state, as for when running is controlled by the underlying platform
t1.start();
// Block, waiting for the T1 thread to finish executing
t1.join();
}
Copy the code
Thread.join() source code analysis
Join (), join(long millis), join(long millis, int nanos)
public final void join(a) throws InterruptedException {}
public final synchronized void join(long millis) throws InterruptedException {}
public final synchronized void join(long millis, int nanos) throws InterruptedException {}
Copy the code
The join() method is equivalent to join(0)
Join (long millis, int nanos) also calls join(long millis)
So let’s use join(Long millis) as an example
/** * wait at most millis milliseconds for the thread to terminate. A timeout of 0 means waiting forever. * This method calls this.watit using a loop to determine isAlive(). When the future terminates, the this.notifyAll method is called. * Applications are advised not to use Wait, notify, or notifyAll * * on Thread instances@paramMillis - Wait time in milliseconds *@throwsIllegalArgumentException - If the value of millis is negative *@throwsInterruptedException - If any thread interrupts the current thread. Clears the interrupted state of the current thread when this exception is thrown. * /
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0); }}else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break; } wait(delay); now = System.currentTimeMillis() - base; }}}Copy the code
And the core of that is
// While (isAlive()) prevents the child thread from spurious wakeup,
// As long as the child thread (T1) does not terminate, the parent thread (main) must continue to wait.
while (isAlive()) {
wait(0);
}
Copy the code
IsAlive () is a final native method
/** * Tests whether the thread is alive. If a thread is started and not dead, it is active * *@returnTrue if the thread is still alive; It is false; * /
public final native boolean isAlive(a);
Copy the code
step
public static void main(String[] args) throws InterruptedException {
// Initialization state
Thread t1 = new Thread(() -> {});
// Runnable/running state, as for when running is controlled by the underlying platform
t1.start();
// Block, waiting for the T1 thread to finish executing
t1.join();
}
Copy the code
-
T1 new Thread(() -> {})
-
Call a t1. Start (); Starting a thread
-
Also call t1.join(); Method, the main thread enters the wait state
- The core code in the Join method is the call
while (isAlive())
isAlive()
The method is to determine whether the T1 thread is finished- According to the above code, the thread is calling an empty method
() - > {}
- so
isAlive()
It’s going to go straight backfalse
- At this time
t1.join();
Method within the code execution, will call the JVM C++ source, executenotifyAll()
- The code returns to the main thread
t1.join();
So let’s go ahead and execute - The end of the
- The core code in the Join method is the call
If the code is modified to
public static void main(String[] args) throws InterruptedException {
// Initialization state
Thread t1 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch(InterruptedException e) { e.printStackTrace(); }});// Runnable/running state, as for when running is controlled by the underlying platform
t1.start();
// Block, waiting for the T1 thread to finish executing
t1.join();
}
Copy the code
So when you call the t1.join() method
- Create thread T1
new Thread(() -> {//... })
- call
t1.start();
Starting a thread - At the same time call
t1.join();
Method, main thread (main
) will enter the wait state- The core code in the Join method is the call
while (isAlive())
isAlive()
The method is to determine whether the T1 thread is finished- Based on the code above, the method executed by the thread takes about a second to complete
- so
isAlive()
It’s going to go straight backtrue
, the callwait()
Continue to wait for - in
while (isAlive())
It’ll be back in a second or sofalse
- At this time
t1.join();
Method completes code execution - The code returns to the main thread
t1.join();
So let’s go ahead and execute - The end of the
- The core code in the Join method is the call
conclusion
After looking at the source code, or relatively easy to understand. However, I could not find any place in the source code where notifyAll is called after executing the join. I searched the Internet for this question and found the final answer in the JVM C++ source code
void JavaThread::run(a) {...thread_main_inner(a); }void JavaThread::thread_main_inner(a) {...this->exit(false);
delete this;
}
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {...ensure_join(this); . }static void ensure_join(JavaThread* thread) {
// We do not need to grap the Threads_lock, since we are operating on ourself.
Handle threadObj(thread, thread->threadObj());
assert(threadObj.not_null(), "java thread object must exist");
ObjectLocker lock(threadObj, thread);
// Ignore pending exception (ThreadDeath), since we are exiting anyway
thread->clear_pending_exception(a);// Thread is exiting. So set thread_status field in java.lang.Thread class to TERMINATED.
java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
// Clear the native thread instance - this makes isAlive return false and allows the join()
// to complete once we've done the notify_all below
java_lang_Thread::set_thread(threadObj(), NULL);
lock.notify_all(thread); / / the key
// Ignore pending exception (ThreadDeath), since we are exiting anyway
thread->clear_pending_exception(a); }Copy the code
The key code is lock.notify_all(thread); This is where notifyAll() is called