Hello everyone, I am Glacier ~~

Today, let’s talk about the order of execution of threads, which may be different from what you imagine

First, the order of execution of threads is uncertain

When threads are started by calling the start() method of Thread, the order of execution is uncertain. That is, after multiple threads are created in a row, the order in which the threads’ start() methods are called does not determine the order in which they are executed.

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

package io.binghe.concurrent.lab03; /** * @author binghe * @version 1.0.0 * @description thread order, */ 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

Class ThreadSort01 creates three different threads, thread1, thread2, and thread3. Next, The thread1.start(), thread2.start(), and thread3.start() methods are called sequentially in the program to start three different threads.

Thread1, thread2 and thread3 are executed in the same order as thread1, thread2 and thread3. Run ThreadSort01’s main method, and the result is shown below.

thread1
thread2
thread3
Copy the code

When you run it again, the result is shown below.

thread1
thread3
thread2
Copy the code

On the third run, the result is shown below.

thread2
thread3
thread1
Copy the code

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

How to ensure the order of execution of threads

1. A simple example of ensuring thread execution order

In real business scenarios, there may be times when the threads started later need to rely on the threads started first to complete the execution of the business logic in the thread. At this point, you need to ensure the order of execution of the threads. So how do you ensure that threads are executed in order?

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 thread order, The thread.join () method ensures the order in which threads are executed */ 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 (); // Actually let the main thread wait for the child thread to complete. thread2.start(); thread2.join(); thread3.start(); thread3.join(); }}Copy the code

As you can see, ThreadSort02 resembles ThreadSort01 by adding the join() method of the calling thread below the start method of each thread. At this point, run the ThreadSort02 class, with the result shown below.

thread1
thread2
thread3
Copy the code

When you run it again, the result is shown below.

thread1
thread2
thread3
Copy the code

On the third run, the result is shown below.

thread1
thread2
thread3
Copy the code

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

2. How does the join method ensure the thread execution order

Since Thread’s join() method ensures Thread order, let’s take a look at what Thread’s join() method is all about.

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

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

You can see that the join() method calls one of the participating Join () methods of the class, passing in the argument 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 parameter is decorated with synchroinzed, indicating that the method can only be called by one instance or method at a time. Since 0 is passed, 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 it has been started and is active, the wait() method of the class is called and 0 is passed. 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 native method that calls underlying JDK methods in JNI’s way to make threads wait for execution to complete.

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

Ok, that’s enough for today. I’m Glacier. See you next time