Two ways to ensure the thread execution order

Introduction

This post would demo how to ensure the thread execution order by using Thread.join or the CountDownLatch.

Environments

  • Java 1.8

Via the Thread.join method

According to JAVA API document, Thread.join let the current thread to wait for the thread for some time, just as follows:

public final void join(long millis) throws InterruptedException

Waits at most millis milliseconds for this thread to die. A timeout of 0 means to wait forever. This implementation uses a loop of this.wait calls conditioned on this.isAlive. As a thread terminates the this.notifyAll method is invoked. It is recommended that applications not use wait, notify, or notifyAll on Thread instances.

And there is a simple join method:

public final void join() throws InterruptedException

Waits for this thread to die.An invocation of this method behaves in exactly the same way as the invocation: join(0)

Thread.join example

We would create three threads and let them execute in this order:

three threads in order

that is:

  • Thread2 starts as soon as thread1 end
  • Thread3 starts 1 second after the the thread2 starts
  • Main thread would wait all threads done

Create a custom Thread class

Here we create a custom thread class with name and sleeping features as follows:

static class MyThread extends Thread {
        private final String name;

        public MyThread(String name) {
            super(name);
            this.name = name;
        }

        public void run() {
            try {
                log.info("thread "+name+" started");
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                log.error("",e);
            }
            log.info("thread "+name+" end");
        }
    }

As you can see, the thread just sleep for 3 seconds and then end.

use the thread.join to control the order of thread execution

Thread thread1 = new MyThread("thread1");
Thread thread2 = new MyThread("thread2");
Thread thread3 = new MyThread("thread3");

log.debug("join test start...");

thread1.start();
thread1.join();     //wait until thread1 is done
thread2.start();
thread2.join(1000); //wait at most 1 second
thread3.start();

thread3.join();     //wait for all the threads done
log.debug("join test all  done");

Pay attention to thread3.join, if you remove this line of code, the main thread would not wait all threads done.

  • thread1 started and sleep 3 seconds
  • thread1.join() called, then main thread would wait until thread1 is done
  • thread2 start and join(1000), it would wait at most 1 second
  • After 1 second, thread3.start called, and thread3.join make the main thread to wait until the thread3 is done

run the code and we got this console output:

20:54:54.051 [main] DEBUG java8.learn.thread.thread_join.JoinTester - join test start...
20:54:54.055 [thread1] INFO java8.learn.thread.thread_join.JoinTester - thread thread1 started
20:54:57.060 [thread1] INFO java8.learn.thread.thread_join.JoinTester - thread thread1 end
20:54:57.061 [thread2] INFO java8.learn.thread.thread_join.JoinTester - thread thread2 started
20:54:58.062 [thread3] INFO java8.learn.thread.thread_join.JoinTester - thread thread3 started
20:55:00.064 [thread2] INFO java8.learn.thread.thread_join.JoinTester - thread thread2 end
20:55:01.065 [thread3] INFO java8.learn.thread.thread_join.JoinTester - thread thread3 end
20:55:01.065 [main] DEBUG java8.learn.thread.thread_join.JoinTester - join test all  done

Process finished with exit code 0

The order is just the same as expected.

CountDownLatch Example

Now we would have one producer thread and one consumer thread, the consumer must wait for the producer complete its job.

producer and consumer in order

what’s CountDownLatch

According to the api, CountDownLatch is

A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

For the initiation of the CountDownLatch:

A CountDownLatch is initialized with a given count. The await methods block until the current count reaches zero due to invocations of the countDown() method, after which all waiting threads are released and any subsequent invocations of await return immediately. This is a one-shot phenomenon – the count cannot be reset. If you need a version that resets the count, consider using a CyclicBarrier.

The constructor:

CountDownLatch(int count) Constructs a CountDownLatch initialized with the given count.

The countDown method:

countDown() Decrements the count of the latch, releasing all waiting threads if the count reaches zero.

The await method:

await() Causes the current thread to wait until the latch has counted down to zero, unless the thread is interrupted.

The CountDownLatch property

private static CountDownLatch latch = new CountDownLatch(1);

We define a CountDownLatch with only one lock , because there is only one producer thread to wait.

The producer thread

static class MyProducerThread extends Thread {
        private final String name;

        public MyProducerThread(String name) {
            super(name);
            this.name = name;
        }

        public void run() {
            try {
                log.info("producer thread "+name+" started");
                Thread.sleep(3000);
                latch.countDown(); //count down the latch
            } catch (InterruptedException e) {
                log.error("",e);
            }
            log.info("producer thread "+name+" end");
        }
    }

Note the latch.countDown() , this method would decrease the latch ,and notify the waiter.

The consumer thread

static class MyConsumerThread extends Thread {
        private final String name;

        public MyConsumerThread(String name) {
            super(name);
            this.name = name;
        }

        public void run() {
            try {
                log.info("consumer thread "+name+" start waiting...");
                latch.await();
                log.info("consumer thread "+name+" started");
            } catch (InterruptedException e) {
                log.error("",e);
            }
            log.info("consumer thread "+name+" end");
        }
    }

We create a consumer thread ,and the key point is as follows:

  • in the run() method, we call latch.await() , this would pause the current thread and wait for the countdown to zero.

The consumer and producer threads execution in order

Thread threadProducer = new MyProducerThread("producer1");
Thread threadConsumer = new MyConsumerThread("consumer1");

log.debug("CountDown test start...");

threadProducer.start();
threadConsumer.start();

threadConsumer.join();

log.debug("CountDown test all  done");

We should notice that:

  • we just call the producer and consumer’s start to run all the threads
  • At last we call the consumer’s join method to wait for the consumer to complete

Let’s run the code ,and we got this output:

21:12:07.829 [main] DEBUG java8.learn.thread.thread_join.JoinTester - CountDown test start...
21:12:07.833 [producer1] INFO java8.learn.thread.thread_join.JoinTester - producer thread producer1 started
21:12:07.833 [consumer1] INFO java8.learn.thread.thread_join.JoinTester - consumer thread consumer1 start waiting...
21:12:10.837 [producer1] INFO java8.learn.thread.thread_join.JoinTester - producer thread producer1 end
21:12:10.837 [consumer1] INFO java8.learn.thread.thread_join.JoinTester - consumer thread consumer1 started
21:12:12.841 [consumer1] INFO java8.learn.thread.thread_join.JoinTester - consumer thread consumer1 end
21:12:12.841 [main] DEBUG java8.learn.thread.thread_join.JoinTester - CountDown test all  done

the key points are as follows:

  • The producer and consumer threads are started almost simultaneously, but the consumer thread just waiting for the producer thread
  • After the producer thread done, the consumer thread start in real
  • After all threads done, the main thread ends

Process finished with exit code 0

It’s so easy, do you think so?

You can find detail documents about the springboot and unit testing here: