Master Thread Execution Order in Java: join, CountDownLatch & SingleThreadExecutor

This tutorial demonstrates four straightforward techniques—using join() in child threads, join() in the main thread, CountDownLatch, and a single‑thread executor—to reliably control Java thread execution order, each illustrated with clear code examples and expected output.

Programmer DD
Programmer DD
Programmer DD
Master Thread Execution Order in Java: join, CountDownLatch & SingleThreadExecutor

This article introduces four simple methods to control the execution order of Java threads, demonstrated with four code examples.

In a child thread using join() to specify order

The join() method blocks the current thread until the specified thread finishes, ensuring a defined sequence.

public class ThreadJoinDemo {
    public static void main(String[] args) throws InterruptedException {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("打开冰箱!");
            }
        });
        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("拿出一瓶牛奶!");
            }
        });
        final Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread2.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("关上冰箱!");
            }
        });
        // The start order can be changed; join() guarantees the execution order.
        thread3.start();
        thread2.start();
        thread1.start();
    }
}

Output:

打开冰箱!
拿出一瓶牛奶!
关上冰箱!

In the main thread using join() to specify order

Calling join() in the main thread blocks it until the target thread completes, enforcing the desired sequence.

public class ThreadMainJoinDemo {
    public static void main(String[] args) throws InterruptedException {
        final Thread thread1 = new Thread(() -> System.out.println("打开冰箱!"));
        final Thread thread2 = new Thread(() -> System.out.println("拿出一瓶牛奶!"));
        final Thread thread3 = new Thread(() -> System.out.println("关上冰箱!"));
        thread1.start();
        thread1.join();
        thread2.start();
        thread2.join();
        thread3.start();
    }
}

Output:

打开冰箱!
拿出一瓶牛奶!
关上冰箱!

Using CountDownLatch to achieve ordering

CountDownLatch provides flexible control: when the counter reaches zero, the waiting thread proceeds without needing to know which thread finished.

public class ThreadCountDownLatchDemo {
    private static CountDownLatch countDownLatch1 = new CountDownLatch(1);
    private static CountDownLatch countDownLatch2 = new CountDownLatch(1);

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            System.out.println("打开冰箱!");
            countDownLatch1.countDown();
        });
        Thread thread2 = new Thread(() -> {
            try {
                countDownLatch1.await();
                System.out.println("拿出一瓶牛奶!");
                countDownLatch2.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread thread3 = new Thread(() -> {
            try {
                countDownLatch2.await();
                System.out.println("关上冰箱!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        // The start order can be changed; CountDownLatch ensures correct output.
        thread3.start();
        thread1.start();
        thread2.start();
    }
}

Output:

打开冰箱!
拿出一瓶牛奶!
关上冰箱!

Using a single‑thread executor (newSingleThreadExecutor())

A single‑thread executor serializes task execution, guaranteeing order.

public class ThreadPoolDemo {
    static ExecutorService executorService = Executors.newSingleThreadExecutor();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> System.out.println("打开冰箱!"));
        Thread thread2 = new Thread(() -> System.out.println("拿出一瓶牛奶!"));
        Thread thread3 = new Thread(() -> System.out.println("关上冰箱!"));
        executorService.submit(thread1);
        executorService.submit(thread2);
        executorService.submit(thread3);
        executorService.shutdown(); // Remember to shut down the executor.
    }
}

Output:

打开冰箱!
拿出一瓶牛奶!
关上冰箱!
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaconcurrencyThreadJOINTutorialExecutorServiceCountDownLatch
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.