In Java, the synchronized keyword and Lock lock are used to realize concurrent access control of resources. Only unique threads are allowed to enter the critical area to access resources (except read locks). The main purpose of this sub-control is to solve the problem of data inconsistency caused by multiple threads concurrently with the same resource. In another scenario, a resource has multiple copies for use at the same time, such as multiple printers in the printer room, and multiple pits for use at the same time. In this case, Java provides another concurrent access control - concurrent access control for multiple copies of the resource, and Semaphore used today is one of them.
Java can detect potential thread safety problems in our system in the fastest way through code simulation. Here, Semaphore (semaphore) and CountDownLatch (locking) are used with ExecutorService (thread pool) for simulation. The main introduction is as follows:
1. Semaphore
This class will be provided after JDK 1.5
Semaphore is a count-based semaphore. It can set a threshold, based on this, multiple threads compete to obtain the license signal, and return it after making their own application. After exceeding the threshold, the thread's application for the license signal will be blocked. Semaphore can be used to build some object pools, resource pools, etc., such as database connection pools. We can also create Semaphore with a count of 1, using it as a mechanism similar to mutex locks. This is also called a binary semaphore, indicating two mutex states.
2. CountDownLatch
This class will be provided after JDK 1.5.
The CountDownLatch class can make a thread wait for other threads to complete their respective work before executing. For example, the main thread of the application wants to execute after the thread responsible for starting the framework service has started all the framework services.
CountDownLatch is implemented through a counter, and the initial value of the counter is the number of threads. Whenever a thread completes its own task, the counter value is decremented by 1. When the counter value reaches 0, it means that all threads have completed the task, and then the threads waiting on the lock can resume the execution of the task.
As shown in the figure below:
The above two classes can be used in combination to achieve the effect of simulating high concurrency. The following code is used to give an example:
package modules;import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;public class CountExample { // Total number of requests public static int clientTotal = 5000; // Number of threads executed concurrently at the same time public static int threadTotal = 200; public static int count = 0; public static void main(String[] args) throws Exception { ExecutorService executorService = Executors.newCachedThreadPool(); //Semiconductor, used to control the number of concurrent threads here final Semaphore semaphore = new Semaphore(threadTotal); //Locking, which can realize the decrement of the counter final CountDownLatch countDownLatch = new CountDownLatch(clientTotal); for (int i = 0; i < clientTotal ; i++) { executorService.execute(() -> { try { //Execute this method to obtain execution permission. When the total number of unreleased licenses does not exceed 200, //Allow passes, otherwise the thread blocks and waits until the license is obtained. semaphore.acquire(); add(); //Release the license semaphore.release(); } catch (Exception e) { //log.error("exception", e); e.printStackTrace(); } //Lock down countDownLatch.countDown(); }); } countDownLatch.await();//Thread blocks, and the block is not released until the lock value is 0. Continue to execute executorService.shutdown(); log.info("count:{}", count); } private static void add() { count++; }}As shown in the above method, 5000 requests are simulated, and 200 concurrent operations are up to 200 concurrent operations at the same time. Observe the final results and find that the results are different for each time and are inconsistent with the expectations. The result is as follows:
22:18:26.449 [main] INFO modules.CountExample - count:4997
22:18:26.449 [main] INFO modules.CountExample - count:5000
22:18:26.449 [main] INFO modules.CountExample - count:4995
22:18:26.449 [main] INFO modules.CountExample - count:4998
Final conclusion: The add method is not thread-safe
Then how to ensure the thread safety of the add method, modify the add method as follows:
private static void add() { count.incrementAndGet();}The execution results are as follows:
22:18:26.449 [main] INFO modules.CountExample - count:5000
22:18:26.449 [main] INFO modules.CountExample - count:5000
22:18:26.449 [main] INFO modules.CountExample - count:5000
22:18:26.449 [main] INFO modules.CountExample - count:5000
22:18:26.449 [main] INFO modules.CountExample - count:5000
22:18:26.449 [main] INFO modules.CountExample - count:5000
22:18:26.449 [main] INFO modules.CountExample - count:5000
22:18:26.449 [main] INFO modules.CountExample - count:5000
Final conclusion: The modified add method thread-safe
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.