Interpretation of the source code of Thread's join method

1, Function

The main function of the join method in the Thread class is synchronization, which can change the parallel execution between threads into serial execution. See code for details:

public class JoinTest {
    public static void main(String [] args) throws InterruptedException {
        ThreadJoinTest t1 = new ThreadJoinTest("Xiao Ming");
        ThreadJoinTest t2 = new ThreadJoinTest("Xiaodong");
        t1.start();
        /**join It means to abandon the execution of the current thread and return the corresponding thread. For example, the following code means:
         The program invokes the middle note join method of the t1 thread in the main thread. Then the main thread abandits cpu control and returns the t1 thread to continue executing until the thread t1 is executed.
         Therefore, the result is that after t1 thread is executed, it can be executed by the main thread, which is equivalent to synchronizing t1 thread in the main thread. After t1 thread is executed, the main thread has the opportunity to execute
         */
        t1.join();
        t2.start();
    }

}
class ThreadJoinTest extends Thread{
    public ThreadJoinTest(String name){
        super(name);
    }
    @Override
    public void run(){
        for(int i=0;i<5;i++){
            System.out.println(this.getName() + ":" + i);
        }
    }
}

Output result:

The middle note also shows the function of join method: when the join() method of the B thread is invoked in the A thread, it is indicated that only when the B thread is executed, can the A thread continue to execute, which is equivalent to turning parallelism into serial. Note that the join method called here does not pass parameters. In fact, the join method can also pass a parameter to it.

If A parameter is passed in the join method, it means that if the join(10) of thread B is used in thread A, it means that thread A will wait for thread B to execute for 10 milliseconds. After 10 milliseconds, threads A and B execute in parallel, but the specific execution depends on the cpu scheduling. It should be noted that the jdk stipulates that join(0) does not mean that thread A waits for thread B for 0 seconds, but that thread A waits for thread B for unlimited time until thread B completes execution, that is, join(0) is equivalent to join().

2, join and start call sequence

The above discussion probably knows the function of join. So what happens if the top note join is called before start? First look at the following test results:

public class JoinTest {
    public static void main(String [] args) throws InterruptedException {
        ThreadJoinTest t1 = new ThreadJoinTest("Xiao Ming");
        ThreadJoinTest t2 = new ThreadJoinTest("Xiaodong");
        /**join The top note can not be synchronized when invoked before the start method.
         */
        t1.join();
        t1.start();
        //Thread.yield();
        t2.start();
    }

}
class ThreadJoinTest extends Thread{
    public ThreadJoinTest(String name){
        super(name);
    }
    @Override
    public void run(){
        for(int i=0;i<1000;i++){
            System.out.println(this.getName() + ":" + i);
        }
    }
}

Output result:

Base note: the join method must be called after the thread start method invocation. This is also easy to understand: if a thread does not start, it will not be able to synchronize.

3, Source code analysis

No more nonsense, go directly to the source code:

public final void join() throws InterruptedException {
        join(0);
    }

The default join method is to pass in a 0 millisecond. See the implementation below:

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;
            }
        }
    }

First pay attention to the method modifier and use the final modifier to indicate that this method cannot be rewritten or overwritten. synchronized maintains the synchronization of threads at the JVM level. Next, take a look at this isAlive. Let's look at the notes:

/**
     * Tests if this thread is alive. A thread is alive if it has
     * been started and has not yet died.
     *
     * @return  <code>true</code> if this thread is alive;
     *          <code>false</code> otherwise.
     */
    public final native boolean isAlive();

The thread is started only, and it is not destroyed until isAlive is in the state of true. In other words, a thread calls the start () method, and there is no death (the blocked thread calls the interrupt() method or the thread is executed), the middle state is true, then the wait () method of JNI is invoked, and the base note interaction mechanism is used to interact with threads.

public final native void wait(long timeout) throws InterruptedException;

In this example, we can also add a line of output statement verification, and the result is the same as we want.

reference resources: https://blog.csdn.net/si444555666777/article/details/82347056

 

Tags: Multithreading

Posted by ieda on Sun, 17 Apr 2022 10:01:58 +0930