The role and principle of Thread.join

Article Introduction

Many people know very little about the role and implementation of Thread.join. After all, we rarely use this API. This article will still be combined with use and principles for in-depth analysis

Content navigation

  1. The role of Thread.join
  2. The realization principle of Thread.join
  3. When will Thread.join be used

The role of Thread.join

Someone asked me an interview question like this before

How to make multiple threads execute in the order specified by themselves in Java?

The simplest answer to this question is through Thread.join. Over time, many people mistakenly believe that Thread.join is used to ensure the order of threads.
The following code demonstrates the role of Thread.join

public class JoinDemo extends Thread{ int i; Thread previousThread; //上一个线程 public JoinDemo(Thread previousThread,int i){ this.previousThread=previousThread; this.i=i; } @Override public void run() { try { //调用上一个线程的join方法,大家可以自己演示的时候可以把这行代码注释掉 previousThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("num:"+i); } public static void main(String[] args) { Thread previousThread=Thread.currentThread(); for(int i=0;i<10;i++){ JoinDemo joinDemo=new JoinDemo(previousThread,i); joinDemo.start(); previousThread=joinDemo; } } } 

In the above code, pay attention to the previousThread.join part. You can comment this line of code to see the effect of the operation afterwards. The result of the operation when there is no join is uncertain. After adding join, the running results are displayed in increasing order.

The meaning of thread.join is that the current thread needs to wait for the previousThread to terminate before returning from thread.join. To put it simply, the thread will block at the join method until the thread finishes executing.

The following figure shows the effect of join on threads

 
TIM picture 20181204180103.png

The realization principle of Thread.join

How are threads blocked? What method is used to wake up? First look at what the Thread.join method does

public class Thread implements Runnable { ... public final void join() throws InterruptedException { join(0); } ... 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) { //判断是否携带阻塞的超时时间,等于0表示没有设置超时时间 while (isAlive()) {//isAlive获取线程状态,无线等待直到previousThread线程结束 wait(0); //调用Object中的wait方法实现线程的阻塞 } } else { //阻塞直到超时 while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } } ... 

From the source code of the join method, the essence of the join method is to call the wait method in the Object to achieve thread blocking. The implementation principle of the wait method will be explained in detail in subsequent articles. But what we need to know is that the call to the wait method must acquire the lock , so the join method is modified by synchronized. The synchronized modification is equivalent to synchronized (this) at the method level. This is an instance of previousThread itself.

Many people do not understand why join is blocking the main thread? The reason for not understanding is that the method of blocking the main thread is placed in the role of previousThread, making everyone mistakenly think that the previousThread should be blocked. In fact, the main thread will hold the lock of the previousThread object, and then call the wait method to block, and the caller of this method is in the main thread. So the main thread is blocked.

The second question, why can the previousThread thread be able to wake up the thread? Or when did it wake up?

To understand this problem, we have to turn to the source code of jdk, but if you have a certain basic understanding of threads, threads blocked by the wait method need to be awakened by notify or notifyall. So there will be a wake-up operation after the thread is executed, but we don't need to care.
Next, find thread.cpp in the source code of hotspot, and see if the thread exits to do related things to prove our conjecture.

void JavaThread::exit(bool destroy_vm, ExitType exit_type) { assert(this == JavaThread::current(), "thread consistency check"); ... // Notify waiters on thread object. This has to be done after exit() is called // on the thread (if the thread is the last thread in a daemon ThreadGroup the // group should have the destroyed bit set before waiters are notified). ensure_join(this); assert(!this->has_pending_exception(), "ensure_join should have cleared"); ... 

Observe the comments on the ensure_join (this) line of code to wake up the thread object that is waiting. This is the cleaning work done after the thread is terminated. The definition of this method is as follows

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(); // 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 //这里是清除native线程,这个操作会导致isAlive()方法返回false java_lang_Thread::set_thread(threadObj(), NULL); lock.notify_all(thread);//注意这里 // Ignore pending exception (ThreadDeath), since we are exiting anyway thread->clear_pending_exception(); } 

In the ensure_join method, call lock.notify_all (thread); to wake up all threads waiting for the thread lock, which means that the main thread blocked by the call of the join method will be woken up; so far, we basically made a comparison of the principle of join detailed analysis

In summary, the bottom layer of Thread.join is to implement thread communication through wait / notifyall to achieve the purpose of thread blocking; when the thread execution ends, two things will be triggered. The first is to set the native thread object to null and the second Through the notifyall method, the wait method waiting on the previousThread object lock is woken up.

When will Thread.join be used

In actual application development, we rarely use thread.join. In actual use, we can wait for the result of the thread execution through the join method, which is actually similar to the function of future / callable.
We use the following pseudo code to illustrate the use scenarios of join

public void joinDemo(){ //.... Thread t=new Thread(payService); t.start(); //.... //其他业务逻辑处理,不需要确定t线程是否执行完 insertData(); //后续的处理,需要依赖t线程的执行结果,可以在这里调用join方法等待t线程执行结束 t.join(); } 


Author: Architect practicing Collection
link: https: //www.jianshu.com/p/fc51be7e5bc0
Source: Jane books
are copyrighted by the author. For commercial reproduction, please contact the author for authorization, and for non-commercial reproduction, please indicate the source.

Guess you like

Origin www.cnblogs.com/cxxiao/p/12683594.html