Multithreaded interview - CountDownLatch, CyclicBarrier, Semaphore

Posted by XPertMailer on Fri, 04 Mar 2022 08:39:25 +0100

0. Summary

1.CountDownLatch refers to one thread waiting for other threads, and CyclicBarrier refers to multiple threads waiting for each other;
2.CountDownLatch is the count - 1 until it is reduced to 0, and CyclicBarrier is the count + 1 until it reaches the specified value;
3.CountDownLatch is disposable and CyclicBarrier is recycled;
4.CyclicBarrier can select 1 to execute 1 priority operation after the last thread arrives;
5.Semaphore can only be executed after obtaining the lock, and can choose fair and unfair modes

1.CountDownLatch

It is a synchronization helper that allows one or more threads to wait until a set of operations performed by other threads are completed.

Construction method

A count value is passed in for counting.

public CountDownLatch(int count) {
	if (count < 0) throw new IllegalArgumentException("count < 0");
	this.sync = new Sync(count);
}

There are two common methods: await and countDown

  • 1.await method
    When a thread calls the await method, it blocks the current thread.
public void await() throws InterruptedException {
	sync.acquireSharedInterruptibly(1);
}
  • 2.countDown method
    Every time a thread calls the countDown method, the count is decremented by 1. When the value of count is equal to 0, the blocked thread will continue to run.
public void countDown() {
	sync.releaseShared(1);
}

Scene design

CountDownLatch blocks the main thread.

  • Main thread: Leader
  • Branch thread: multiple worker s

Now imagine a scenario. There is an urgent bug in the company's project online. When the customer complains, the leader comes anxiously and wants to find someone to quickly solve the bug.

Well, it must be slow to solve it alone, so the leader (main thread) called Zhang San and Li Si to solve it together. Finally, when both of them have finished the tasks they need to do, the leader (main thread) can reply to the customer, and the customer will be relieved.

The main thread can lead the main class to execute the bug, and then we can continue to simulate the main class of Worker until the main class of Worker is finished.

  • If one person fixes the bug, it takes 5 seconds. Now Zhang SangAn takes 2 seconds and Li Sigan takes 3 seconds, and it takes only 3 seconds to complete it.
public class CountDownTest {
    static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(2);
        Worker w1 = new Worker("Zhang San", 2000, latch);
        Worker w2 = new Worker("Li Si", 3000, latch);
        w1.start();
        w2.start();

        long startTime = System.currentTimeMillis();
        latch.await();
        System.out.println("bug After all the problems are solved, the leader can hand over the work to the customer. The task takes a total of time:"+ (System.currentTimeMillis() - startTime));

    }

    static class Worker extends Thread{
        String name;
        int workTime;
        CountDownLatch latch;

        public Worker(String name, int workTime, CountDownLatch latch) {
            this.name = name;
            this.workTime = workTime;
            this.latch = latch;
        }

        @Override
        public void run() {
            System.out.println(name+"Start repair bug,Current time:"+sdf.format(new Date()));
            doWork();
            System.out.println(name+"End repair bug,Current time:"+sdf.format(new Date()));
            latch.countDown();
        }

        private void doWork() {
            try {
                //Simulation time consuming
                Thread.sleep(workTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2.CyclicBarrier

A group of threads wait for each other until all threads reach a synchronization point.
*It's like a group of people trapped in front of a fence. They can break through the fence only after the last person arrives.

1. Two construction methods

1. The first parameter, parties (int type), means that several threads need to arrive together before all threads can cancel waiting.

public CyclicBarrier(int parties) {
    this(parties, null);
}

2. The second parameter barrierAction (Runnable type) is added to construction method 2, which is used to give priority to the execution of barrierAction when all threads reach the barrier.

public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties;
    this.count = parties;
    this.barrierCommand = barrierAction;
}

2. Common methods - await()

  • await();
    In the following example, execute barrier.com once Await(), counter + 1. When the counter value reaches the set value of CyclicBarrier (3 in this example), everyone will break through the barrier together.

3. Scene simulation

1. Running competition (ignoring the referee) - when everyone is ready, run together
  • Using construction method 1
class BarrierTest {
    public static void main(String[] args) {
    	1.Parameter: 4. Quality requires 4 threads to arrive before running
        CyclicBarrier barrier = new CyclicBarrier(4);  //①
        2.Create four athletes: Zhang San, Li Si, Wang Wu and Zhao Liu
        Runner runner1 = new Runner(barrier, "Zhang San");
        Runner runner2 = new Runner(barrier, "Li Si");
        Runner runner3 = new Runner(barrier, "Wang Wu");
        Runner runner4 = new Runner(barrier, "Zhao Liu");
        
		3..Create thread pool
        ExecutorService service = Executors.newFixedThreadPool(4);
        service.execute(runner1);
        service.execute(runner2);
        service.execute(runner3);
        service.execute(runner4);

        service.shutdown();

    }

}
	2.Athletes
class Runner implements Runnable{

    private CyclicBarrier barrier;
    private String name;

    public Runner(CyclicBarrier barrier, String name) {
        this.barrier = barrier;
        this.name = name;
    }

    @Override
    public void run() {
        try {
            1.Simulation preparation time
            Thread.sleep(new Random().nextInt(5000));
            System.out.println(name + ":prepare OK");
            2.Barrier waiting
            barrier.await();
            System.out.println(name +": Run");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e){
            e.printStackTrace();
        }
    }
}

2. Running race – when ready, wait for the referee to whistle

  • Use construction method 2 - add priority execution action (this example is: wait for the referee to whistle)
    When all the athletes are ready, they wait for the referee to whistle.

  • The code above is modified as follows:

1.Create action waiting for referee
Judge waiteJudge = new Judge();
2.Call construction method 2
CyclicBarrier barrier = new CyclicBarrier(4,waiteJudge);

3.Establish referee Juge class
class Judge implements Runnable{
    @Override
    public void run() {
        try {
            System.out.println("Wait for the referee to whistle...");
            Pausing for two seconds here is more convenient to observe the sequence of thread execution
            Thread.sleep(2000);
            System.out.println("The referee whistled->>>>>");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

4. Recycling of cyclicbarrier

Zhang San, Li Si, Wang Wu and Zhao Liu started in two batches.

  • Code modification: the parameter passed in by CyclicBarrier is 2, and the number of core threads in the thread pool is also 2
    .

After two people start running (i.e. after releasing two core threads), the other two talents begin to prepare.

CyclicBarrier barrier = new CyclicBarrier(2,waiteJudge);
ExecutorService service = Executors.newFixedThreadPool(2);

3.Semaphore

Semaphore semaphore is used to control the number of threads that resources can be accessed at the same time. It can generally be used for flow control.

1. Construction method (non fair lock by default)

Method 1: the parameter permits represents the number of semaphores;

public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}

Method 2: the parameter boolean fair, which is true, indicates that a fair lock is used; false: indicates an unfair lock.

(the fair lock mechanism is realized through AQS)

public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

2. Scene design

  • 20 vehicles pass through a bridge, and only 5 vehicles can be on the bridge at the same time. More than 5 vehicles will cause the bridge to collapse.
  • Five locks are set based on Semaphore. When the car gets the lock, it can go on the bridge, return the lock after crossing the bridge, and then take the lock from the car behind.
class SemaphoreTest {
	1.20 cars in total
    private static int count = 20;
    public static void main(String[] args) {
		2.Number of established core threads
        ExecutorService executorService = Executors.newFixedThreadPool(count);
        3.Use semaphore: specify that at most 4 threads can execute at the same time
        Semaphore semaphore = new Semaphore(5);
        4.random Time spent simulating each vehicle
        Random random = new Random();
        5.Submit 20 threads to the thread pool in turn
        for (int i = 0; i < count; i++) {
            final int no = i;
            Car car = new Car(semaphore,no,random);
            executorService.execute(car);
        }
        6.Close thread pool
        executorService.shutdown();
    }
}

1.New automobile
class Car implements Runnable{
    Semaphore semaphore;
    int no;
    Random random;

    public Car(Semaphore semaphore, int no, Random random) {
        this.semaphore = semaphore;
        this.no = no;
        this.random = random;
    }

    @Override
    public void run() {
        try {
            1.Get permission
            semaphore.acquire();
            System.out.println(no +":Car No. 1 is passable");
            2.Simulated vehicle passage time
            Thread.sleep(random.nextInt(2000));
            3.Release license
            semaphore.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Topics: Java Multithreading