Abstract: Today, we will look at how threads are executed, and what is the order of them?

Thread execution order is not what you think!! , author: Ice River.

First, the execution order of threads is uncertain

When the Thread’s start() method is called to start the Thread, the order in which the Thread is executed is uncertain. That is, the order in which start() methods are called does not determine the order in which threads are executed after multiple threads are created consecutively in the same method.

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

package io.binghe.concurrent.lab03; /** * @author binghe * @version 1.0.0 * @description 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(); }}Copy the code

We create three different threads in class ThreadSort01, thread1, thread2, and Thread3. Next, The thread1.start(), thread2.start(), and thread3.start() methods are called in the program in order to start three different threads.

So the question is, do the threads execute in the order of thread1, Thread2, and Thread3? Run the main method of ThreadSort01, and the result is as follows.

thread1
thread2
thread3
Copy the code

When run again, the result is as follows.

thread1
thread3
thread2
Copy the code

On the third run, the result is as follows.

thread2
thread3
thread1
Copy the code

As you can see, each time the program is run, the threads may execute in a different order. The starting order of a thread does not determine the order in which the thread is executed.

How to ensure the execution order of threads

1. A simple example to ensure that threads execute in order

In a real business scenario, sometimes the last started thread may need to rely on the first started thread to complete in order to properly execute the business logic in the thread. At this point, you need to ensure the order in which the threads are executed. So how do you ensure the order of execution of threads?

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

package io.binghe.concurrent.lab03; /** * @author binghe * @version 1.0.0 * @description */ 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(); Thread1.join (); thread1.join(); thread2.start(); thread2.join(); thread3.start(); thread3.join(); }}Copy the code

As you can see, ThreadSort02, like ThreadSort01, adds the join() method of the calling thread below the start method of each thread. At this point, run the ThreadSort02 class, and the result is as follows.

thread1
thread2
thread3
Copy the code

When run again, the result is as follows.

thread1
thread2
thread3
Copy the code

On the third run, the result is as follows.

thread1
thread2
thread3
Copy the code

As you can see, the result of each run is the same, so using Thread’s join() method ensures that the threads are executed in the same order.

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

Since Thread’s join() method ensures the order in which threads execute, let’s take a look at what Thread’s join() method really is.

Enter Thread’s join() method, as shown below.

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

You can see that the join() method calls a join() method of its class that takes a parameter, passing in 0. Follow up with 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; }}}Copy the code

As you can see, the join() method with a long argument uses the synchroinzed modifier, indicating that the method can only be called by one instance or method at a time. Since the parameter passed is 0, the program enters the following code logic.

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

First, the code uses a while loop to determine whether the current thread has been started and is active. If so, the wait() method in the same class is called, passing 0. Follow up with the wait() method, as shown below.

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

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

Note that a call to a thread’s wait() method leaves the main thread in a wait state, waiting for the child thread to finish executing and then execute down again. That is, in the main() method of ThreadSort02, a call to the join() method of a child thread blocks the execution of main(), and when the child is finished, the main() method moves down, starting a second child thread and executing its business logic, and so on.

Click follow to learn about the fresh technologies of Huawei Cloud