preface
In the previous two articles Synchronization queue for AQS,Conditional queue This article is to take a look at the three main concurrency tool classes in JUC: countDownLatch, cyclicBarrier and semaphore. This article will not expand from the perspective of source code as in the previous two articles, but take a look at the usage methods and scenarios of these tool classes in combination with the API of tools.
countDownLatch
countDownLatch can be understood as a counter. The initial value of the counter is the number of times a thread can execute (this is actually not equal to the number of threads, because a thread can execute multiple times). Whenever a thread is executed, the counter value is - 1. When the counter value is 0, it means that all threads are executed. These threads will be blocked before countDownLatch is reduced to 0. These threads will be stopped only when countDownLatch is reduced to 0.
countDownLatch generally has two usage scenarios: it enables a thread to wait for other threads to complete their work before continuing to execute; Of course, it can also be reversed. Other threads execute these threads after waiting for a thread to complete its work. ok, let's simulate these two scenarios.
Scenario 1: now there is a six person meeting. When six people gather, the meeting starts:
public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newCachedThreadPool(); //The simulation needs enough six people to start the meeting CountDownLatch countDownLatch = new CountDownLatch(6); //count down for (int i=0;i<6;i++){ int finalI = i+1; executorService.execute(()->{ try { Thread.sleep((long) (Math.random()*100)); System.out.println("personnel"+ finalI +"We have arrived in the conference room"); countDownLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }); } countDownLatch.await(); System.out.println("Everyone is here. The meeting is over"); executorService.shutdown(); }
There are two main methods of countdownlatch: countDown and await. countDown can reduce the defined countdownlatch value by one at a time. Await is used to block. The following programs of await will be executed only when the value of countdownlatch is reduced to 0.
Operation results:
Person 6 has arrived in the conference room Personnel 3 has arrived in the conference room Person 4 has arrived in the conference room Person 5 has arrived in the conference room Person 1 has arrived in the conference room Person 2 has arrived in the conference room Everyone is here. The meeting is over
What happens if I execute five threads at this time? As we said above, the value of countDownLatch will not be reduced to 0 at this time, so the program under await will not be executed.
Person 1 has arrived in the conference room Personnel 3 has arrived in the conference room Person 5 has arrived in the conference room Person 2 has arrived in the conference room Person 4 has arrived in the conference room
The second scenario: we simulate the running competition of athletes. We need to wait until the referee gives orders and start running
public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(1); for (int i=0;i<6;i++){ int finalI = i+1; new Thread(()->{ try { countDownLatch.await(); Thread.sleep((long) Math.random()*1000); System.out.println("Athletes"+ finalI +"Run to the end"); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } //Simulated three second preparation time Thread.sleep(3000); countDownLatch.countDown(); System.out.println("Referee starting gun"); }
cyclicBarrier
cyclicBarrier can be translated into loop fence and loop barrier. It can make a group of threads wait for each other and carry out subsequent operations after all threads reach a barrier point. I should have seen seven dragon balls when I was a child. Only when the seven dragon balls are gathered can we summon the dragon. Next, let's take seven dragon balls as an example to show the cyclicBarrier.
public static void main(String[] args) throws BrokenBarrierException, InterruptedException { CyclicBarrier barrier=new CyclicBarrier(7,()->{ System.out.println("Collect seven dragon balls to summon the divine dragon"); }); for (int i=1;i<=7;i++){ int num=i; new Thread(()->{ try { System.out.println("Collected page"+num+"Dragon Ball"); barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } },String.valueOf(i)).start(); } }
It can be seen that when new CyclicBarrier, it has two parameters, one is the value of CyclicBarrier, and the other is a runnable function, which means that all threads will execute the runnable function when they reach the fence.
See the following effect:
The third dragon ball has been collected The sixth dragon ball has been collected The first dragon ball has been collected The Seventh Dragon ball has been collected The fourth dragon ball has been collected The fifth dragon ball has been collected The second dragon ball has been collected Collect seven dragon balls to summon the divine dragon
We said earlier that the CyclicBarrier is a circular fence, but this example does not seem to reflect its circular characteristics, but some modifications can be made on this basis:
public static void main(String[] args) throws BrokenBarrierException, InterruptedException { CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{ System.out.println("Gather seven dragon balls and summon the dragon"); System.out.println("The dragon has been summoned. Collect dragon beads again three years later"); }); for (int i=0;i<7;i++){ int finalI = i; new Thread(()->{ try { System.out.println(Thread.currentThread().getName()+"Collected to page"+ finalI +"Dragon Ball"); cyclicBarrier.await(); Thread.sleep(2000); System.out.println(Thread.currentThread().getName()+"Received to page"+finalI+"Dragon Ball"); cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }).start(); } }
In this way, when all threads reach the fence, they will reset the CyclicBarrier.
Thread-1 Collected the first Dragon Ball Thread-4 The fourth dragon ball was collected Thread-2 The second dragon ball was collected Thread-5 The fifth dragon ball was collected Thread-6 The sixth dragon ball was collected Thread-0 Collected the 0th Dragon Ball Thread-3 The third dragon ball was collected Gather seven dragon balls and summon the dragon The dragon has been summoned. Collect dragon beads again three years later Thread-5 Received the fifth Dragon Ball Thread-6 Received the 6th Dragon Ball Thread-3 Received the third dragon ball Thread-2 Received the second Dragon Ball Thread-4 Received the fourth Dragon Ball Thread-0 Received the 0 Dragon Ball Thread-1 Received the first Dragon Ball Gather seven dragon balls and summon the dragon The dragon has been summoned. Collect dragon beads again three years later
Through the above example, it is found that countDownLatch and CyclicBarrier are similar in use. Since they are similar, just use countDownLatch.
Of course, there are still some differences. First, countDownLatch can only be used once, while CyclicBarrier can be recycled. This is also reflected in the above example.
Secondly, the CyclicBarrier counter is controlled by itself, while the CountDownLatch counter is controlled by the user. In the CyclicBarrier, the thread calling the await method will not only block itself, but also reduce the counter by 1. In the CountDownLatch, the thread calling the await method will only block itself without reducing the counter value. Moreover, CyclicBarrier also provides more methods and is more flexible to use.
semaphore
Semaphore means semaphore and semaphore. Semaphore can be used to control the number of threads accessing specific resources at the same time, and ensure the rational use of resources by coordinating each thread.
For example, it can be understood as the display screen standing at the entrance of our parking lot. For every car entering the parking lot, the display screen will show the remaining parking spaces minus 1. For every car leaving the parking lot, the remaining vehicles displayed on the display screen will increase 1. When the remaining parking spaces on the display screen are 0, the vehicles cannot enter the parking lot, Until a car goes out of the parking lot. It can also be seen that semaphore can be used in scenarios with quantitative access restrictions. Next, let's simulate how to use semaphore with code.
public static void main(String[] args) { Semaphore semaphore=new Semaphore(3);//Available resources for (int i=1;i<=10;i++){ new Thread(()->{ try { System.out.println("===="+Thread.currentThread().getName()+"Come to the parking lot"); if (semaphore.availablePermits()==0){ System.out.println("There is no available parking space in the parking lot"); } semaphore.acquire(); System.out.println(Thread.currentThread().getName()+"Successfully entered the parking lot"); TimeUnit.SECONDS.sleep(1); semaphore.release(); System.out.println(Thread.currentThread().getName()+"Leave the parking lot"); } catch (InterruptedException e) { e.printStackTrace(); } },String.valueOf(i)+"Car No").start(); } }
When using Semaphore, the core methods are acquire and release. The acquire method can be regarded as obtaining a resource, while the release method releases a resource. In addition, it also provides some other methods to make our use of Semaphore more flexible.
====4 Car No. 1 came to the parking lot ====9 Car No. 1 came to the parking lot ====1 Car No. 1 came to the parking lot ====8 Car No. 1 came to the parking lot 9 Car No. 1 successfully entered the parking lot ====7 Car No. 1 came to the parking lot ====3 Car No. 1 came to the parking lot There is no available parking space in the parking lot ====2 Car No. 1 came to the parking lot There is no available parking space in the parking lot ====6 Car No. 1 came to the parking lot There is no available parking space in the parking lot ====5 Car No. 1 came to the parking lot There is no available parking space in the parking lot There is no available parking space in the parking lot There is no available parking space in the parking lot 1 Car No. 1 successfully entered the parking lot 4 Car No. 1 successfully entered the parking lot ====10 Car No. 1 came to the parking lot There is no available parking space in the parking lot 1 Car No. 1 leaves the parking lot 3 Car No. 1 successfully entered the parking lot 9 Car No. 1 leaves the parking lot 2 Car No. 1 successfully entered the parking lot 4 Car No. 1 leaves the parking lot 6 Car No. 1 successfully entered the parking lot 3 Car No. 1 leaves the parking lot 5 Car No. 1 successfully entered the parking lot 2 Car No. 1 leaves the parking lot 7 Car No. 1 successfully entered the parking lot 6 Car No. 1 leaves the parking lot 8 Car No. 1 successfully entered the parking lot 7 Car No. 1 leaves the parking lot 10 Car No. 1 successfully entered the parking lot 5 Car No. 1 leaves the parking lot 8 Car No. 1 leaves the parking lot 10 Car No. 1 leaves the parking lot
From the above results, it is still in line with the introduction of semaphore.
ending
The above are some usage methods and scenarios of the three concurrent tool classes. Generally speaking, they are not complex, but they can't be seen without practice. I think we can deepen our understanding by writing some examples or using them in the project on the basis of these examples.