preface

In the daily development process, the development of multithreaded high concurrency program is certainly essential, but it is not easy to apply the multithreaded technology properly.

With the continuous iteration of Java versions, more and more concurrent tools have been introduced, especially since JDK1.5, which greatly reduces the burden of developers on the one hand and improves the efficiency of high concurrent program execution on the other.

Starting from today’s article, I will explain the basic usage and usage scenarios of JDK1.8 concurrent tools, as well as precautions. For these concurrent tools, I will explain the source code after the entire concurrent core library system.


CountDownLatch

“CountDownLatch is a synchronization assistant that allows one or more threads to wait for a series of other threads to complete.”

CountDownLatch is very simple to use, just a utility class that gracefully handles the situation where the main task is waiting for all sub-tasks to complete before moving on to the next step.

The specific steps are as follows:

  1. Counter threshold set by constructor (can’t be less than 0)
  2. The countDown() method, which decrement the count counter specified by CountDownLatch construction by one; If the counter in CountDownLatch is already 0, in which case the countDown() method is ignored and the count cannot be reduced to less than 0
  3. The await() method blocks the currently calling thread until count is reduced to 0, and other threads can interrupt the current thread; Likewise, if count is already 0, call await() and it is ignored and the current thread will not block.
  4. Await (long timeout, TimeUnit unit) method, is a blocking method with the ability of timeout. After a given value of time is reached, the current thread will exit the block regardless of the count value.
  5. The getCount() method returns the value of the current counter.

Its common API is above these, then let’s think about the use of scenarios and actual combat.

Since CountDownLatch has a feature that allows threads to wait for threads to execute, do you have any scenarios in mind?

Let me start with a little bit of context.

Have you ever been asked a question during the interview: How does your system handle orders that fail to pay back?

Have you ever been asked that?

I used to ask candidates this when I was an interviewer at a company; If you haven’t been asked, you probably haven’t met me! Ha, ha, ha.

Different candidates gave different answers to this question; There are also candidates who do not know what the payment callback is, including the whole payment process is not clear.


A brief introduction to wechat payment process

Take the main process of wechat Payment as an example:

1. Our system calls the wechat unified ordering interface (in this process, we will encapsulate appId, merchant number, merchant order number, order amount, currency type, commodity information, openId, callback address and other related parameters and generate signatures)

2. The above interfaces will return payment packet information such as status code and signature, and we will send the payment packet to the front end

3. The front end will pull wechat Pay through the payment SDK with the payment message

4. If the user has completed the payment

5. The wechat system will notify our system through the callback address configured in the first step

6. Our system gets the payment result and postprocesses the order

That’s the basic process of wechat Pay. Let’s go back to the interviewer’s question.

Why would an interviewer ask such a question?

Why did the callback fail?

1. When the merchant background, network and server are abnormal, the merchant system does not receive the payment notice.

2. The callback interface of our system is buggy and abnormal during the process

3, and many puzzling questions

When the above problems occur, it actually causes another problem: order payment timeout.

In general, for unpaid orders, they will be automatically cancelled after 15 minutes. Therefore, if the order status is still pending payment due to this special reason, there will definitely be partial unilateral account if the order is closed directly (for such users, the order has been paid, but the system shows pending payment).

So what can be done about it?

Wechat merchant platform provides an interface to query orders, so that we can make final confirmation of orders and decide the subsequent operations of orders based on the final payment status.

The following is the open API list of wechat merchant platform and the official website address. Those who participate in payment or want to learn payment can read the official document:

Pay.weixin.qq.com/wiki/doc/ap…


To solve the problem

The above has introduced the wechat payment process and the system’s fault tolerance mechanism for problems. What is the relationship with CountDownLatch?

When we check the unpaid orders and need to query the wechat order query interface, if the efficiency of simple circular processing is too low, we can choose to use CountDownLatch and multithreading together to improve the execution efficiency. Let’s look at the code:

@Slf4j
public class CountDownLatchOrderPay {
    /** * analog injection interface */
    private final static WeChatPayService weChatPayService = new WeChatPayService();
    /** * simulates a list of order numbers to be paid */
    private final static List<String> ORDER_LIST = Arrays.asList("F13246"."F468986"."F78546"."F57824"."F7996312"."F78544536"."F578458624"."F799637812"."F77898546"."F57824564"."F79963451312");

    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(ORDER_LIST.size());
        ORDER_LIST.forEach(
                value -> {
                    new Thread(
                            () -> {
                                try {
                                    Map<String, String> result = weChatPayService.checkPay(value);
                                } catch (Exception e) {
                                    log.error("Exception Handling");
                                } finally{ latch.countDown(); } } ).start(); });try {
            log.info("Program waiting for batch call wechat: {}", Thread.currentThread().getName());
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("Get the result set and proceed to the next task: {}", Thread.currentThread().getName());
    }

    public static class WeChatPayService {
        /** * <p> * https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no} * </p> * *@paramOutTradeNo Merchant Order Number *@returnSimulated wechat returns the result */
        public Map<String,String> checkPay(String outTradeNo) {
            try {
                // Simulate the call time
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("Call the order query interface of wechat merchant platform, and the order number is {}", outTradeNo);
            HashMap<String, String> result = new HashMap<>();
            result.put("out_trade_no", outTradeNo);
            result.put("trade_state"."SUCCESS");
            returnresult; }}}Copy the code

The above code simply simulates the scenario where the wechat interface is called concurrently and the main thread waits for the unified execution result of the sub-threads. This is a solution, and you can use it flexibly.

Note: Due to its nature, the await() method waits for count to decrease to 0, so WHEN I call countDown(), I put it in the finally block to prevent count from decreasing to 0 due to exceptions, which would result in the system waiting forever. I can also use the timeout wait mechanism properly.

I created the thread through a loop, which is not recommended. Please use a thread pool to create and manage threads. Why did I create it this way? This is actually a little bit of a tip (please forgive me), but I’ll talk more about thread pools later, so I won’t do it here.


Another way to play it

When I introduced CountDownLatch, I mentioned that it allows one or more threads to wait

In the case of simulating order processing, we use the main thread to wait for the child thread to complete execution; So how do you make multiple threads wait for one thread to finish?

A lot of people probably think of CountDownLatch as “the starting gun,” right, right; You can really form a starting gun pattern;

Just like in track and field, all the competitors wait at the starting line, and when the judge fires the starting gun, all start at the same time.

public static void main(String[] args) {
  //count is set to 1
  CountDownLatch latch = new CountDownLatch(1);
  IntStream.range(0.10).forEach(
    value -> {
      new Thread(
        () -> {
          try {
            // Wait for the call
            latch.await();
            log.info("Current thread: {}, start...", Thread.currentThread().getName());
          } catch (InterruptedException e) {
            log.error("Exception handling..."); } } ).start(); });try {
    // The main thread waits for 3 seconds
    TimeUnit.SECONDS.sleep(3);
  } catch (InterruptedException e) {
    e.printStackTrace();
  } finally {
    log.info("Ready go...");
    // Launchlatch.countDown(); }}Copy the code

The result is as follows:

[the main] INFO com. Xiaozhi. The latch. CountDownLatchTest - Ready go... [Thread - 0] INFO com. Xiaozhi. Latch. CountDownLatchTest - the current Thread: Thread - 0, to run... [Thread - 8] INFO com. Xiaozhi. Latch. CountDownLatchTest - the current Thread: Thread - 8, to run... [Thread - 7] INFO com. Xiaozhi. Latch. CountDownLatchTest - the current Thread: Thread - 7, to run... [Thread - 6] INFO com. Xiaozhi. Latch. CountDownLatchTest - the current Thread: Thread - 6, to run... [Thread - 5] INFO com. Xiaozhi. Latch. CountDownLatchTest - the current Thread: Thread - 5, to run... [Thread - 9] INFO com. Xiaozhi. Latch. CountDownLatchTest - the current Thread: Thread - 9, to run... [Thread - 3] INFO com. Xiaozhi. Latch. CountDownLatchTest - the current Thread: Thread - 3, to run... [Thread - 2] INFO com. Xiaozhi. Latch. CountDownLatchTest - the current Thread: Thread 2, to run... [Thread 1] INFO com. Xiaozhi. Latch. CountDownLatchTest - the current Thread: Thread 1, to run... [Thread - 4] INFO com. Xiaozhi. Latch. CountDownLatchTest - the current Thread: Thread - 4, to run...Copy the code

Looking at the code above, we set the initial value of CountDownLatch to 1, create 10 threads to wait, call countDown() after the main thread sleeps for 3 seconds, and execute on all child threads.

Readers, do you find CountDownLatch easy to use?

However, have you noticed that after count changes from the initial value to 0, the role of latch as a whole ends and cannot be reused? Let’s look at CyclicBarrier in the next section, which is similar to CountDownLatch but can be reused.

That’s the end of CountDownLatch for today, and I’ll see you in the next article!

Public Portal — CountDownLatch for Concurrency

The code cloud code link is as follows:

Gitee.com/songyanzhi/…

Thanks for reading, and wish you all a happy and healthy work!