Concurrency tools - introduction and use of CountDownLatch, CyclicBarrier, Semaphore, Exchanger

CountDownLatch waiting for multithreading to complete

CountDownLatch is similar to join, which is to make the current thread wait for other threads to end.

The implementation principle of join is to constantly check whether the thread is alive.

while (isAlive()) {
    
    
    wait(0);
}

The difference between CountDownLatch and join:

CountDownLatch allows the waiting thread to only wait for the end of a certain step of the worker thread, and does not need to let the worker thread complete its execution.

Example:

public class CountDownLatchTest {
    
    
    
    staticCountDownLatch c = new CountDownLatch(2);
    
    public static void main(String[] args) throws InterruptedException {
    
    
        new Thread(new Runnable() {
    
    
        @Override
            public void run() {
    
    
                System.out.println(1);
                c.countDown();
                System.out.println(2);
                c.countDown();
            }
        }).start();
        c.await();
        System.out.println("3");
    }
}

The constructor of CountDownLatch receives a parameter of type int as a counter. If you want to wait for N points to complete, pass in N here.

When we call the countDown method of CountDownLatch ,N will be reduced by 1, the await method of CountDownLatchwill block the current thread until N becomes zero. Since the countDown method can be used anywhere, the N points mentioned here can be N threads, or N execution steps in one thread. When used in multiple threads, you only need to pass the CountDownLatch reference to the thread.

Synchronous barrier CyclicBarrier

CyclicBarrier literally means recyclable (Cyclic) barrier (Barrier).

The function is to block a group of threads when they reach a barrier (also called a synchronization point). The barrier will not open until the last thread reaches the barrier, and all threads blocked by the barrier will continue to run. (game match)

Introduction to Cyclic Barriers

The default construction method of CyclicBarrier is CyclicBarrier(int parties), whose parameter indicates the number of threads intercepted by the barrier ,Each thread calls the await method to tell the CyclicBarrier that I have reached the barrier, and then the current thread is blocked.

Example:

public class CyclicBarrierTest {
    
    
    staticCyclicBarrier c = new CyclicBarrier(2);
    public static void main(String[] args) {
    
    
        new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    c.await();
                } catch (Exception e) {
    
    

                }
                System.out.println(1);
             }
        }).start();
        try {
    
    
            c.await();
        } catch (Exception e) {
    
    
            
        }
        System.out.println(2);
    }
}

output:

1
2
或者
2
1

If the parameter of CyclicBarrier is 3, it will always block because no three threads arrive.

CyclicBarrier also provides a more advanced constructor CyclicBarrier(int parties, Runnable barrierAction ), which is used inWhen a thread reaches the barrier, execute barrierAction first, which is convenient for handling more complex business scenarios, as shown in the following code.

import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierTest2 {
    
    
    
    static CyclicBarrier c = new CyclicBarrier(2, new A());
    
    public static void main(String[] args) {
    
    
        new Thread(new Runnable() {
    
    
        @Override
            public void run() {
    
    
                try {
    
    
                    c.await();
                } catch (Exception e) {
    
    

                }
                System.out.println(1);
            }
        }).start();
        
        try {
    
    
            c.await();
        } catch (Exception e) {
    
    

        }
        System.out.println(2);
    }
    
    static class A implements Runnable {
    
    
    @Override
        public void run() {
    
    
            System.out.println(3);
        }
    }
}

output:

3
1
2

The difference between CyclicBarrier and CountDownLatch

  • The counter of CountDownLatch can only be used once, while the counter of CyclicBarrier can be reset using the reset() method

    CyclicBarrier can handle more complex business scenarios. For example, if a calculation goes wrong, the counter can be reset and the thread executed again.

  • CyclicBarrier also provides other useful methods

    For example, the getNumberWaiting method can obtain the number of threads blocked by Cyclic-Barrier. The isBroken () method is used to know whether the blocked thread is interrupted.

Semaphore that controls the number of concurrent threads

Semaphore (semaphore) is used to control the number of threads that access specific resources at the same time. It coordinates each thread to ensure the reasonable use of public resources.

public class SemaphoreTest {
    
    
    
    private static final int THREAD_COUNT = 30;

    private static ExecutorServicethreadPool = Executors
    .newFixedThreadPool(THREAD_COUNT);
	//只有10个数据库连接
    private static Semaphore s = new Semaphore(10);

    public static void main(String[] args) {
    
    
        for (inti = 0; i< THREAD_COUNT; i++) {
    
    
            threadPool.execute(new Runnable() {
    
    
            @Override
                public void run() {
    
    
                    try {
    
    
                        s.acquire();
                        System.out.println("save data");
                        s.release();
                    } catch (InterruptedException e) {
    
    

                    }
                }
            });
        }
        threadPool.shutdown();
    }
}

The use of Semaphore is also very simple. First, the thread uses the acquire method of Semaphore to obtain a license, and then calls the release() method to return the license after use . You can also try to acquire a license with the tryAcquire method.

Exchanger for exchanging data between threads

Exchanger (Exchanger) is a tool class for collaboration between threads . Exchanger is used for data exchange between threads.

  • It provides a synchronization point where two threads can exchange data with each other. These two threads exchange data through the exchange method. If the first thread executes the exchange() method first, it will wait for the second thread to also execute the exchange method. When both threads reach the synchronization point, the two threads will be synchronized. You can exchange data and pass the data produced by this thread to the other party.

Application scenarios of Exchanger:

  • Exchanger can be used in genetic algorithm: In the genetic algorithm, two people need to be selected as mating objects. At this time, the data of the two people will be exchanged, and two mating results will be obtained using the crossover rule
  • Exchanger can also be used for proofreading work: for example, we need to manually enter paper-based bank statements into electronic bank statements. In order to avoid mistakes, two people from AB and AB posts are used for entry. After entering into Excel, the system needs to load the two Excel , and proofread the two Excel data to see if the input is consistent
public class ExchangerTest {
    
    
    
    private static final Exchanger<String>exgr = new Exchanger<String>();

    private static ExecutorServicethreadPool = Executors.newFixedThreadPool(2);

    public static void main(String[] args) {
    
    
        
        threadPool.execute(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    String A = "银行流水A"; // A录入银行流水数据
                    exgr.exchange(A);
                } catch (InterruptedException e) {
    
    
                    
                }
            }
        });
        threadPool.execute(new Runnable() {
    
    
            @Override
            public void run() {
    
    

                try {
    
    
                    String B = "银行流水B"; // B录入银行流水数据
                    String A = exgr.exchange("B");
                    System.out.println("A和B数据是否一致:" + A.equals(B) + ",A录入的是:"
                    + A + ",B录入是:" + B);
                } catch (InterruptedException e) {
    
    

                }
                
            }
        });
        threadPool.shutdown();
    }
}

If one of the two threads does not execute the exchange method, it will wait forever. If you are worried about special circumstances, you can use exchange (V x, longtimeout, TimeUnit unit) to set the maximum waiting time.

Reference: "The Art of Java Concurrent Programming"

Guess you like

Origin blog.csdn.net/qq_53578500/article/details/126223922