Semaphore source code analysis of Java Concurrent series

Semaphore (semaphore) is a commonly used class in JUC package. It is an application of AQS sharing mode. It can allow multiple threads to operate shared resources at the same time, and can effectively control the concurrency. It can be used to realize traffic control. Semaphore provides the concept of a license, which can be regarded as a bus ticket. Only those who successfully obtain the ticket can get on the bus, and there are a certain number of tickets, which can not be issued without limit, which will lead to bus overload. So when the tickets are issued (the bus is full), others can only wait for the next bus. If someone gets off on the way, his position will be free, so if others want to get on at this time, they can get a ticket again. Various pools can be realized by using semaphore. We will create a simple database connection pool at the end of this article. First, let's look at the constructor of semaphore.

Semaphore provides two constructors with parameters and no constructors without parameters. Both constructors must pass in an initial number of licenses. The semaphore constructed by constructor 1 will be obtained in an unfair way when obtaining the license. Constructor 2 can specify the way to obtain the license (fair or unfair) through parameters. Semaphore mainly provides two types of APIs: obtaining and releasing licenses. The default is to obtain and release one license, or you can pass in parameters to obtain and release multiple licenses at the same time. In this article, we only talk about obtaining and releasing one license at a time.

1. Obtaining licenses

The above API is the default license acquisition operation provided by semaphore. Only one license is obtained at a time, which is also a common situation in real life. In addition to direct acquisition, it also provides attempted acquisition. Direct acquisition may block the thread after failure, while attempted acquisition will not. It should also be noted that the tryacquire method is an unfair attempt to obtain. We usually use the acquire method to obtain licenses. Let's take a look at how it is obtained. You can see that the acquire method directly calls sync Acquireshared interruptibly (1). This method is the method in AQS. We talked about it when talking about the AQS source code series. Now let's review it again.

The acquiressaredinterruptible method first calls the tryacquerishared method to try to obtain. Tryacquerishared is an abstract method in AQS. Fairsync and nonfairsync derived classes implement the logic of this method. Fairsync implements the logic of fair access, while nonfairsync implements the logic of unfair access.

Note that the tryacquireshared method of nonfairsync directly calls the nonfairtryacquireshared method, which is in the parent class sync. The logic of unfair lock acquisition is to first take out the current synchronization status (synchronization status indicates the number of licenses), subtract the parameters from the current synchronization status, and if the result is not less than 0, it proves that there are still available licenses, then directly use CAS operation to update the value of synchronization status, and finally return the result value regardless of whether the result is less than 0. Here, we need to understand the meaning of the return value of the tryacquireshared method. A negative number means that the acquisition failed, zero means that the current thread succeeded but the subsequent threads can no longer obtain it, and a positive number means that the current thread succeeded and the subsequent threads can obtain it. Let's look at the code of the acquiresharedinterruptible method.

If the remaining returned is less than 0, it means that the acquisition fails. Therefore, tryacquisureshared (ARG) < 0 is true, so the doacquisuresharedinterruptible method will be called next. This method, which we talked about when talking about AQS, will wrap the current thread as a node and put it at the end of the synchronization queue, and it may suspend the thread. This is also the reason why threads queue and block when remaining is less than 0. If the remaining > = 0 is returned, it means that the current thread is successful. Therefore, tryacquireshared (ARG) < 0 is false, so the doacquiresharedinterruptible method will not be called to block the current thread. The above is the whole logic of unfair acquisition. Before fair acquisition, just call the hasqueuedpredecessors method to determine whether someone in the synchronization queue is queuing. If so, return - 1 directly indicates that the acquisition fails. Otherwise, continue to perform the same steps as unfair acquisition.

2. Release permit

Calling the release method is to release a license. Its operation is very simple. It calls the releaseshared method of AQS. Let's take a look at this method.

The releaseshared method of AQS first calls the tryrereleaseshared method to try to release the lock. The implementation logic of this method is in the subclass sync.

You can see that the tryrereleaseshared method uses a for loop to spin. First, obtain the synchronization status, add the synchronization status to the incoming parameters, and then update the synchronization status in CAS. If the update is successful, it returns true and jumps out of the method. Otherwise, continue the loop until it is successful. This is the process of semaphore releasing the license.

3. Write a connection pool

The semaphore code is not very complex. The common operation is to obtain and release a license. The implementation logic of these operations is also relatively simple, but this does not hinder the wide application of semaphore. Now let's use semaphore to implement a simple database connection pool. Through this example, we hope readers can better master the application of semaphore.

Test code:

Test results:

We use an array to store the references of database connections. When initializing the connection pool, we will call the initconnections method to create a specified number of database connections and store their references in the array. In addition, there is an array of the same size to record whether the connections are available. Whenever an external thread requests to get a connection, it first calls semaphore The acquire () method obtains a license, then sets the connection status to in use, and finally returns a reference to the connection. The number of licenses is determined by the parameters passed in during construction. Each call to semaphore The number of licenses of the acquire () method is reduced by 1. When the number is reduced to 0, it indicates that there is no connection available. At this time, if other threads get it again, it will be blocked. Whenever a thread releases a connection, it calls semaphore Release () releases the license. At this time, the total number of licenses will increase, which means that the number of available connections has increased. Then the previously blocked thread will wake up and continue to obtain the connection. At this time, it can obtain the connection successfully after obtaining it again. In the test example, a connection pool of three connections is initialized. We can see from the test results that whenever a thread obtains a connection, the number of remaining connections will be reduced by 1. When it is reduced to 0, other threads can no longer obtain it. At this time, you must wait for a thread to release the connection before continuing to obtain it. We can see that the number of remaining connections always changes between 0 and 3, indicating that our test is successful.

The above is the whole content of this article. I hope it will be helpful to your study, and I hope you can support programming tips.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>