Difference between locking and fence and applicable scenarios

Posted by rsanc on Tue, 16 Jun 2020 04:34:12 +0200

Opening

I believe that little partners know more or less about these two words. They are common thread communication tools in concurrent programming. The two are very similar, but they are different, which leads to a lot of confusion among many small partners, including me: what is the difference between them, and what scenarios are they applicable to?
Now listen to me slowly. If you don't want to see the example or process, you can pull it to the bottom to see the summary

atresia

Count down latch is commonly known as counter. Official explanation:

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

A synchronization helper that allows one or more threads to wait until the completion of a set of operations performed in another thread.

In other words, there can be one or more threads, waiting for other threads to complete an operation before continuing.
What do you mean? Here's a chestnut:
In our life, we should often meet a situation. When we take a bus, especially at the departure station, the driver's master will wait until the number of passengers on the bus reaches a certain level in order to pull more passengers at a time. The test code is as follows:

public static void main(String[] args) {
        List<Passenger> list = new ArrayList<>();
        Passenger p1 = new Passenger("Reading the meeting book");
        Passenger p2 = new Passenger("Watch mobile phones");
        Passenger p3 = new Passenger("See the scenery");
        Passenger p4 = new Passenger("See the conductor");
        list.add(p1);
        list.add(p2);
        list.add(p3);
        list.add(p4);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(20, 200, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), new ThreadFactory() {
            private ThreadGroup group = (null == System.getSecurityManager() ? Thread.currentThread().getThreadGroup() : System.getSecurityManager().getThreadGroup());
            private AtomicInteger num = new AtomicInteger();
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(group, r,"zoo" + num.getAndIncrement(),0);
                thread.setDaemon(false);
                return thread;
            }
        }, new ThreadPoolExecutor.CallerRunsPolicy());
        //Set latching release threshold
        CountDownLatch countDownLatch = new CountDownLatch(list.size());
        log.error("The driver needs one car to start. Let's wait for someone...");
        for (Passenger p : list) {
            executor.execute(()->gotoZOO(p,countDownLatch));
        }
        try {
            countDownLatch.await();
            log.error("Enough people, take off!");
            executor.shutdown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    private static void  gotoZOO(Passenger p,CountDownLatch countDownLatch){
        log.error("{}Our passengers are getting on",p.getDoWhat());
        try {
            countDownLatch.countDown();
            log.error("{}",p.doWhatOnBus());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static class Passenger{
        private String doWhat;

        public Passenger(String doWhat) {
            this.doWhat = doWhat;
        }

        public String getDoWhat() {
            return doWhat;
        }

        public String doWhatOnBus() {
            return "It's so boring in the car,"+doWhat+"Come on!";
        }
    }

results of enforcement

23:46:34.698 [main] ERROR  com.test  -The driver needs one car to start again. Let's wait
 23:46:34.757 [zoo1] ERROR  com.test  -See the mobile phone passengers get on the bus
 23:46:34.758 [zoo3] ERROR  com.test  -See the conductor get on the bus
 23:46:34.757 [zoo0] ERROR  com.test  -The book reader got on the bus
 23:46:34.759 [zoo1] ERROR  com.test  -It's so boring in the car. Look at your cell phone!
23:46:34.759 [zoo3] ERROR  com.test  -It's so boring in the car. Let's see the conductor!
23:46:34.757 [zoo2] ERROR  com.test  -The scenic passenger got on the bus
 23:46:34.759 [zoo0] ERROR  com.test  -It's so boring in the car. Read books!
23:46:34.759 [zoo2] ERROR  com.test  -It's so boring in the car. Look at the scenery!
23:46:34.759 [main] ERROR  com.test  -Enough people, take off!

The driver (master thread) has to wait for 4 passengers before she starts to leave the car (waiting for the 4 sub threads to finish the countDown after calling it). And when passengers get on the train (calling countDown), they should do what they do and do their own things. They will not be foolish enough to stop doing anything because they get on the bus. (they will not block themselves by calling countDown). When the driver sees enough people (reaches the set threshold), he will start.

Locking summary:
After the main thread calls await, it will block waiting for other sub threads to call countDown method to reduce the threshold value to 0, and then continue to execute.
The child thread will not block because the countDown method is called

fence

Official explanation of the fence:

/**
 * A synchronization aid that allows a set of threads to all wait for
 * each other to reach a common barrier point.  CyclicBarriers are
 * useful in programs involving a fixed sized party of threads that
 * must occasionally wait for each other. The barrier is called
 * <em>cyclic</em> because it can be re-used after the waiting threads
 * are released.
 */
Synchronization help allows a group of threads to wait for each other to reach a common barrier. CyclicBarriers are useful in programs involving a fixed size thread side, which sometimes have to wait for each other. The barrier is called < EM > cyclic < / EM > because it can be reused after releasing the waiting thread.

From the class annotation, we can roughly understand that it is used in a group, that is, multiple threads. When all threads reach a certain state, they block until all threads reach a certain state, and then continue to execute. And it can be reused.

The above description is still too obscure. Let's take a chestnut:
When we were children, our school organized spring outings, set the place, and when we arrived, we went in together. Write a simple example to see how the fence works in this scene

public static void main(String[] args) {
        List<Boy> list = new ArrayList<>();
        Boy boy1 = new Boy("Look at the tiger");
        Boy boy2 = new Boy("Watch orangutans");
        Boy boy3 = new Boy("Watch the lion");
        Boy boy4 = new Boy("See the conductor");
        list.add(boy1);
        list.add(boy2);
        list.add(boy3);
        list.add(boy4);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(20, 200, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), new ThreadFactory() {
            private ThreadGroup group = (null == System.getSecurityManager() ? Thread.currentThread().getThreadGroup() : System.getSecurityManager().getThreadGroup());
            private AtomicInteger num = new AtomicInteger();
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(group, r,"zoo" + num.getAndIncrement(),0);
                thread.setDaemon(false);
                return thread;
            }
        }, new ThreadPoolExecutor.CallerRunsPolicy());
        //Initialize fence, set barrier threshold
        CyclicBarrier cyclicBarrier = new CyclicBarrier(list.size());
        for (Boy boy : list) {
            executor.execute(()->gotoZOO(boy,cyclicBarrier));
        }
    }

    private static void  gotoZOO(Boy boy,CyclicBarrier cyclicBarrier){
        log.error("We haven't arrived yet. Wait a minute,{}'s little boy starts to wait",boy.getWhere());
        try {
            cyclicBarrier.await();
            log.error("{}",boy.goWhere());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static class Boy{
        private String where;
        public Boy(String where) {
            this.where = where;
        }

        public String getWhere() {
            return where;
        }

        public String goWhere() {
            return "We're all here. I'm going"+where+"la";
        }
    }

Execution result:

22:05:59.476 [zoo2] ERROR  com.test  -People haven't arrived yet. Wait a minute. The little boy watching the lion begins to wait
 22:05:59.477 [zoo1] ERROR  com.test  -People haven't arrived yet. Wait a minute. The little boy watching the orangutan begins to wait
 22:05:59.477 [zoo0] ERROR  com.test  -People haven't arrived yet. Wait a minute. The little boy watching the tiger begins to wait
 22:05:59.476 [zoo3] ERROR  com.test  -I haven't arrived yet. Wait a minute. The little boy of the conductor begins to wait
 22:05:59.484 [zoo0] ERROR  com.test  -I'm going to see the tiger!
22:05:59.484 [zoo2] ERROR  com.test  -I'm going to see the lion!
22:05:59.484 [zoo3] ERROR  com.test  -I'm going to see the conductor!
22:05:59.484 [zoo1] ERROR  com.test  -I'm going to see the orangutans!

We can find that the first three little boys didn't enter the zoo after they arrived, but four little boys didn't enter the zoo until the fourth little boy arrived. Before that, one more little boy waited for each little boy (each thread called the await method), until all the people arrived (thread blocked waiting to reach the barrier point 4), each little boy The child goes back to the zoo to see the animals (each thread continues to perform its own task). It's like a fence at the gate of a zoo. It's a group ticket. You have to let children in every time when people arrive.

Fence summary
Each sub thread waits for each other until it reaches the threshold value of fence initialization

Differentiation and personal understanding

Blocking: it is similar to a statistical function (maybe this is why it is also commonly known as a counter). The main thread calls the await method to block and wait for the statistical results, while the sub thread is only responsible for calling the countDown method to tell the main thread that I am OK without blocking itself when the statistical requirements are met; there is a receiving result (main thread) and one or more sending quantity (sub thread) Process);
Fence: first, when a thread calls the await method, it will block the current thread. Second, I understand that it has no master child relationship like locking. It means that each thread waits for each other and continues to execute when it reaches a certain point.

Applicable scenarios

In fact, we can see from the above distinction: if it is necessary to summarize the interfaces whether the multithreaded execution is completed or not to a certain thread, and then continue the execution, for example, if each thread calculates an indicator, and then calculates the sum of all indicators or other indicators after the completion of calculation, then blocking can be used;
If only each thread needs to wait for each thread to finish, and then continue to do its own work, you can use fences. For example, three threads of ABC need to obtain 123 three indicators respectively, and then A needs to take the average of these three numbers, B needs to take the total, and C needs to take the variance. Then you need to wait for ABC to take 123 three indicators before you can calculate. At this time, fences can be used.

summary

Both of them are very good thread communication tools, but the details are different.
All in all:

Locking is to wait for the execution result of another thread in one thread;
And fences are threads waiting for each other, and then starting to do their own things at the same time

last

The code in this article is just for a better explanation of the difference between the two tools. If you don't write well, please forgive me a lot. If you find anything wrong, you are welcome to leave a message. Let's make progress together!

Topics: Java Mobile P4 less Programming