“This is the second day of my participation in the Gwen Challenge in November. See details: The Last Gwen Challenge in 2021”

Hello, I’m looking at the mountains.

Most of the time, we expect to implement a feature that starts a few child threads in the main thread and waits for all child threads to finish executing before the main thread continues. For example, the boss assigns tasks and many workers start to work. After all the workers finish their work, the boss inspects them.

Solution analysis:

  1. The main thread uses join to wait for all child threads to complete and continue execution.
  2. The main thread knows the number of child threads, the number of unfinished child threads, and waits for all child threads to complete before resuming execution.

Implementation via Join

The first way is to call the Java API’s join method on a thread and wait for it to terminate, which can be implemented directly.

Each worker is a thread whose run method is the implementation of the job. Each worker is identified by name, with the following code:

import java.util.Random;
import java.util.concurrent.TimeUnit;

public class Worker extends Thread {
    private String workerName;

    public Worker(String workerName) {
        this.workerName = workerName;
    }

    / * * *@see java.lang.Thread#run()
        */
    @Override
    public void run(a) {
        System.out.println(this.workerName + "At work. ..");
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
        } catch (InterruptedException e) {
        }
        System.out.println(this.workerName + "Job done!");
    }
    
    public String getWorkerName(a) {
        returnworkerName; }}Copy the code

The boss recruits workers, puts them to work, and then waits for them to finish, checking the results of their work (capitalists). , the specific code is as follows:

import java.util.List;

public class Boss {
    private List<Worker> workers;

    public Boss(List<Worker> workers) {
        System.out.println("The boss takes workers.");
        this.workers = workers;
    }

    public void work(a) {
        System.out.println("The boss began to put the workers to work. ..");
        for (Worker worker : workers) {
            System.out.println("Arranged by the boss" + worker.getWorkerName() + "Work");
            worker.start();
        }
        System.out.println("The boss arranged for the work to end. ..");

        System.out.println("The boss is waiting for all the workers to finish. ...");
        for (Worker w : workers) {
            try {
                w.join();
            } catch (InterruptedException e) {
            }
        }
        System.out.println("The workers have finished their work and the boss has begun to inspect them!"); }}Copy the code

Now write the main method to test:

import java.util.ArrayList;
import java.util.List;

public class JoinDemo {
    public static void main(String[] args) {
        Worker w1 = new Worker("Zhang");
        Worker w2 = new Worker("Bill");
        Worker w3 = new Worker("Fifty");
        
        List<Worker> workers = new ArrayList<Worker>();
        workers.add(w1);
        workers.add(w2);
        workers.add(w3);
        
        Boss boss = new Boss(workers);
        boss.work();
        
        System.out.println("End of main method"); }}Copy the code

The execution result is as follows:

The boss recruited workers. The boss began to put the workers to work. . Boss Arranges Sam's Work Boss Arranges Sam's Work Zhang SAN is working. . The boss arranges Wang Wu's work Li Si is working. . The boss arranged for the work to end. . The boss is waiting for all the workers to finish. . Wang Wu is working. . King five is done! Zhang SAN is done! Tom's done with his work! The workers finished their work, and the boss began to inspect! The main method EndsCopy the code

This is implemented through CountDownLatch

The second way is to implement a counter for counting the total number of child threads and the number of unfinished threads. When the number of unfinished threads is about 0, the main thread waits. When the number of outstanding threads equals 0, the main thread continues execution.

Of course, now that we’re thinking about this approach, the Java API team is also thinking that JDK 1.5 provides CountDownLatch for implementing the above method.

Therefore, the above worker method is modified:

import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class Worker extends Thread {
    private CountDownLatch downLatch;
    private String workerName;

    public Worker(CountDownLatch downLatch, String workerName) {
        this.downLatch = downLatch;
        this.workerName = workerName;
    }

    public void run(a) {
        System.out.println(this.workerName + "At work. ..");
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
        } catch (InterruptedException ie) {
        }
        System.out.println(this.workerName + "Job done!");
        this.downLatch.countDown();
    }
    
    public String getWorkerName(a) {
        returnworkerName; }}Copy the code

Latch.countdown (), which is used to decrement the counter by one at the end of the child thread execution, i.e., the number of outstanding child threads.

The boss class should also be modified accordingly:

import java.util.List;
import java.util.concurrent.CountDownLatch;

public class Boss {
    private List<Worker> workers;
    private CountDownLatch downLatch;

    public Boss(List<Worker> workers, CountDownLatch downLatch) {
        this.workers = workers;
        this.downLatch = downLatch;
    }

    public void work(a) {
        System.out.println("The boss began to put the workers to work. ..");
        for (Worker worker : workers) {
            System.out.println("Arranged by the boss" + worker.getWorkerName() + "Work");
            worker.start();
        }
        System.out.println("The boss arranged for the work to end. ..");
        
        System.out.println("The boss is waiting for all the workers to finish. ...");
        try {
            this.downLatch.await();
        } catch (InterruptedException e) {
        }
        System.out.println("The workers have finished their work and the boss has begun to inspect them!"); }}Copy the code

Latch.await () waits for the child thread to finish.

Write the main method to verify:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(3);

        Worker w1 = new Worker(latch, "Zhang");
        Worker w2 = new Worker(latch, "Bill");
        Worker w3 = new Worker(latch, "Fifty");
        
        List<Worker> workers = new ArrayList<Worker>();
        workers.add(w1);
        workers.add(w2);
        workers.add(w3);

        Boss boss = new Boss(workers, latch);
        
        boss.work();
        
        System.out.println("End of main method"); }}Copy the code

The execution result is as follows:

The boss began to put the workers to work. . Boss Arranges Sam's Work Boss Arranges Sam's Work Zhang SAN is working. . The boss arranges Wang Wu's work Li Si is working. . The boss arranged for the work to end. . The boss is waiting for all the workers to finish. . Wang Wu is working. . King five is done! Tom's done with his work! Zhang SAN is done! The workers finished their work, and the boss began to inspect! The main method EndsCopy the code

Use a CyclicBarrier

There is also an implementation that does not block the main thread, but listens for all child threads to terminate. When used here in the worker-boss scenario above, the code is as follows:

Worker:

import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;

public class Worker extends Thread {
    private String workerName;
    private CyclicBarrier barrier;

    public Worker(String workerName, CyclicBarrier barrier) {
        this.workerName = workerName;
        this.barrier = barrier;
    }

    @Override
    public void run(a) {
        System.out.println(this.workerName + "At work. ..");
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
        } catch (InterruptedException e) {
        }
        System.out.println(this.workerName + "Job done!");

        try {
            barrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch(BrokenBarrierException e) { e.printStackTrace(); }}public String getWorkerName(a) {
        returnworkerName; }}Copy the code

The boss:

import java.util.List;

public class Boss {
    private List<Worker> workers;

    public Boss(List<Worker> workers) {
        this.workers = workers;
    }

    public void work(a) {
        System.out.println("The boss began to put the workers to work. ..");
        for (Worker worker : workers) {
            System.out.println("Arranged by the boss" + worker.getWorkerName() + "Work");
            worker.start();
        }
        System.out.println("The boss arranged for the work to end. ..");
        
        System.out.println("The boss is waiting for all the workers to finish. ..."); }}Copy the code

Main method test:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(3.new Runnable() {
            @Override
            public void run(a) {
                System.out.println("The workers have finished their work and the boss has begun to inspect them!"); }}); Worker w1 =new Worker("Zhang", barrier);
        Worker w2 = new Worker("Bill", barrier);
        Worker w3 = new Worker("Fifty", barrier);

        List<Worker> workers = new ArrayList<Worker>();
        workers.add(w1);
        workers.add(w2);
        workers.add(w3);
        
        Boss boss = new Boss(workers);
        boss.work();

        System.out.println("End of main method"); }}Copy the code

The execution result is as follows:

The boss began to put the workers to work. . Boss Arranges Sam's Work Boss Arranges Sam's Work Zhang SAN is working. . The boss arranges Wang Wu's work Li Si is working. . The boss arranged for the work to end. . The boss is waiting for all the workers to finish. . Wang Wu is working. . Li Si's work is done! King five is done! Zhang SAN is done! The workers finished their work, and the boss began to inspect!Copy the code

It is clear from the result analysis that the main method starts executing after the work method of the boss object finishes executing. The boss is waiting for all the workers to finish the work. …” “Wang Wu is working. ..” “, followed by “End of main”, because there is even a chance that “end of main” will be printed at the end of CPU preemption.

Let’s say we have a feature where we need to batch write some records to the database and record when that operation is used, but we don’t want to affect other operations (that is, we don’t want to block the main thread), CyclicBarrier comes in handy.


Hello, I’m looking at the mountains. Swim in the code, play to enjoy life. If this article is helpful to you, please like, bookmark, follow. Welcome to follow the public account “Mountain Hut”, discover a different world.