[high concurrency] deeply understand the execution sequence of threads

Hello, I'm glacier~~

Recently, readers often ask me: Glacier, in what order are threads executed? In order to answer everyone's question, I'll write a separate article today. Well, no more. Let's get to today's topic.

1, The execution order of threads is uncertain

When calling the start() method of Thread to start the Thread, the execution order of the Thread is uncertain. That is to say, in the same method, after successive multiple threads are created, the order of the start() method of the calling Thread does not determine the execution sequence of the Thread.

For example, here, look at a simple example program, as shown below.

package io.binghe.concurrent.lab03;

/**
 * @author binghe
 * @version 1.0.0
 * @description The order of threads, directly call thread The execution of the start () method cannot ensure the execution order of threads
 */
public class ThreadSort01 {
    public static void main(String[] args){
        Thread thread1 = new Thread(() -> {
            System.out.println("thread1");
        });
        Thread thread2 = new Thread(() -> {
            System.out.println("thread2");
        });
        Thread thread3 = new Thread(() -> {
            System.out.println("thread3");
        });

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

Three different threads are created in ThreadSort01 class, thread1, thread2 and thread3. Next, thread1 is called in sequence in the program start(),thread2.start() and thread3 Start () method to start three different threads.

So, the question is, do threads execute in the order of thread1, thread2 and thread3? Run the main method of ThreadSort01, and the results are as follows.

thread1
thread2
thread3

When run again, the results are as follows.

thread1
thread3
thread2

On the third run, the results are as follows.

thread2
thread3
thread1

You can see that the execution order of threads may be different each time you run the program. The starting order of threads does not determine the execution order of threads.

2, How to ensure the execution order of threads

1. Simple example of ensuring thread execution order

In the actual business scenario, sometimes, the thread that is started later may need to rely on the thread that is started first to complete the execution in order to correctly execute the business logic in the thread. At this point, you need to ensure the execution order of threads. So how to ensure the execution order of threads?

You can use the join() method in the Thread class to ensure the execution order of threads. For example, the following test code.

package io.binghe.concurrent.lab03;

/**
 * @author binghe
 * @version 1.0.0
 * @description Sequence of threads, thread The join () method ensures the execution order of threads
 */
public class ThreadSort02 {
    public static void main(String[] args) throws InterruptedException {

        Thread thread1 = new Thread(() -> {
            System.out.println("thread1");
        });
        Thread thread2 = new Thread(() -> {
            System.out.println("thread2");
        });
        Thread thread3 = new Thread(() -> {
            System.out.println("thread3");
        });

        thread1.start();

        //Actually, let the main thread wait for the execution of the child thread to complete
        thread1.join();

        thread2.start();
        thread2.join();

        thread3.start();
        thread3.join();
    }
}

It can be seen that ThreadSot02 is similar to ThreadSort01 class, and the join() method of calling thread is added under the startup method of each thread. At this point, run the ThreadSort02 class, and the results are as follows.

thread1
thread2
thread3

When run again, the results are as follows.

thread1
thread2
thread3

On the third run, the results are as follows.

thread1
thread2
thread3

As you can see, the result of each run is the same. Therefore, the join() method of Thread can ensure the sequential execution order of threads.

2. How does the join method ensure the execution order of threads

Since the join() method of Thread class can ensure the execution order of threads, let's see what the join() method of Thread class is.

Enter the join() method of Thread, as shown below.

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

You can see that the join() method calls a parameterized join() method of the same kind and passes the parameter 0. Continue to follow up the code as shown 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;
        }
    }
}

You can see that the join() method with a long type parameter uses the synchronized modifier, indicating that this method can only be called by one instance or method at a time. Since the parameter passed is 0, the program will enter the following code logic.

if (millis == 0) {
    while (isAlive()) {
        wait(0);
    }
}

First, in the code, judge whether the current thread has been started and is in the active state by means of a while loop. If it has been started and is in the active state, call the wait() method in the same class and pass the parameter 0. Continue to follow up the wait() method, as shown below.

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

As you can see, the wait() method is a local method that calls the JDK underlying method through JNI to make the thread wait for execution to complete.

It should be noted that when calling the thread's wait() method, the main thread will be in a waiting state and execute downward again after the execution of the child thread is completed. That is to say, in the main() method of ThreadSort02 class, calling the join() method of the child thread will block the execution of main() method. When the child thread is executed, the main() method will continue to execute down, start the second sub threads, and execute the logic of the child thread, and so on.

Keywords: Multithreading Concurrent Programming

Added by xfezz on Tue, 18 Jan 2022 21:14:14 +0200