Preface
In the previous articleAbstractQueuedSynchronizer implementation principle - 1, we explained the acquisition and release of exclusive synchronization state in AbstractQueuedSynchronizer, and here we start to explain the acquisition and release of shared synchronization state in AbstractQueuedSynchronizer.
Shared Synchronous State Acquisition and Release
Shared lock as the name implies is that multiple threads can share a lock, use acquireShared in the synchronizer to get the shared lock (synchronous state), the source code of the method is as follows.
First try to get the shared lock by tryAcquireShared, the method is a template method in the synchronizer just throw an unsupported operation exception, need developers to implement their own, while the return value of the method has three different types respectively represent three different states, the meaning of the following.
- less than 0 means the current thread failed to obtain a lock
- equal to 0 means that the current thread succeeded in acquiring the lock, but the subsequent threads will fail to acquire the lock without releasing it, which means that the lock is the last lock in the shared mode.
- greater than 0 means the current thread succeeded in acquiring the lock and there are still locks left to acquire
When the method tryAcquireShared returns less than 0, which means that the lock acquisition failed, the method doAcquireShared will be executed to follow up the method.
|
|
method first calls the addWaiter method to encapsulate the current thread and the node whose wait status is shared module and add it to the wait synchronization queue, you can find that the nextWaiter property of the node in shared mode is a fixed value Node.SHARED. If the return value is greater than or equal to 0, then the setHeadAndPropagate method is called to update the head node and propagate backward to wake up the successor node if there are available resources. If the interrupt has occurred, the current thread is interrupted and the method returns at the end. If the return value is less than 0, it means that the lock acquisition failed and the current thread needs to be hung to block or continue to spin to acquire the shared lock. Here is the implementation of the setHeadAndPropagate method.
|
|
First set the node that currently gets the lock as the head node, then the method parameter propagate > 0 means that the return value of the previous tryAcquireShared method is greater than 0, which means that there is still a remaining shared lock to get, then get the successor node of the current node and wake up the node when the successor node is a shared node to try to get the lock, doReleaseShared method is the main logic for synchronizer shared lock release.
The synchronizer provides the releaseShared method for releasing a shared lock, the source code of which is shown below.
First call the tryReleaseShared method to try to release the shared lock, the method returns false which means the lock release failed, the method ends with false, otherwise it means the lock was successfully released, then execute the doReleaseShared method to wake up the successor node and check if it can propagate backwards, etc. Continue with the method as follows.
|
|
As you can see, unlike exclusive lock release, in shared mode, state synchronization and release can be performed simultaneously, with atomicity guaranteed by CAS, and the loop will continue if the head node changes. Every time a shared node wakes up in shared mode, the head node points to it, so that all subsequent nodes that have access to the shared lock are guaranteed to wake up.
How to customize synchronization components
Most of the synchronizer-based classes in the JDK aggregate one or more classes that inherit from the synchronizer, use the template methods provided by the synchronizer to customize the internal synchronization state management, and then use this internal class to implement the synchronization state management function, which in fact uses the template pattern in a way. For example, the reentrant lock ReentrantLock, read/write lock ReentrantReadWriteLock, semaphore Semaphore and synchronization tool class CountDownLatch in the JDK have the following source code screenshots.

As we know from the above, we can customize the exclusive lock synchronization component and the shared lock synchronization component based on synchronizers. Lock interface to provide user-oriented methods, such as calling the lock method to obtain a lock, using unlock to release the lock, etc.
Inside the TripletsLock class there is a custom synchronizer Sync inherited from the synchronizer AQS, used to control the access and synchronization state of the thread, when the thread calls the lock method When a thread calls the lock method, the custom synchronizer Sync first calculates the synchronization state after the lock is acquired, and then uses the Unsafe class to ensure the atomicity of the synchronization state update. When a thread succeeds in acquiring the lock, the synchronization state state will be minus 1, and when a thread succeeds in releasing the lock, the synchronization state will be plus 1. The range of the synchronization state is 0, 1, 2 and 3, and a synchronization state of 0 means that no synchronization resources are available, so if a thread accesses it, it will be blocked. Here is the code to implement this custom synchronization component.
|
|
Let’s start a test with 20 threads to see if the custom synchronization tool class TripletsLock meets our expectations. The test code is as follows.
|
|
The test results are as follows.

From the above test results, we can find that only three threads can get the lock at the same moment, as expected, and it should be clear here that this lock acquisition process is non-fair.
Summary
This article analyzes the basic data structures in the synchronizer, the process of obtaining and releasing exclusive and shared synchronization state. AbstractQueuedSynchronizer is the basic framework for implementing multi-threaded concurrency tools, and being familiar with it will help us to better use its features and related tools.