AQS principle
AQS (ABstractQueuedSynchronizer) is a framework for blocking locks and related synchronizer tools
Features:
Use the state attribute to represent the status of the resource (exclusive and shared). The subclass needs to define how to maintain this status.
Control how locks are acquired and released
getState - Get the state
setSate - set state
compareAndSetState - Optimistic lock set state
Exclusive: only one thread can access Shared: multi-threaded access
Provides a FIFO-based waiting queue, similar to Monitor's EntryList
Condition variables are used to implement waiting and wake-up mechanisms, and support multiple condition variables, similar to Monitor's WaitSet.
tryAcquire() acquires the lock
tryRelease() releases the lock
The bottom layer uses park and unpark to block and wake up threads
ReentrantLock principle
Principle of unfair lock implementation:
Threads that fail in competition will enter the Node queue. The waitStatus of Node is 0 by default. The creation of Node is lazy.
Yes, the first Node is Dummy or Sentinel, which is used to occupy space and is not associated with threads.
acquireQueued: After entering Node, Thread-1 will continuously try to acquire the lock in an infinite loop and fail.
After entering park blocking, if the previous node of the current Node is head, its status will be changed to -1, indicating that it is responsible for calling
Wake up a node and try to acquire the lock again. If it fails, block the thread.
Lock competition is successful:
No race conditions:
After the lock is released, check whether waitSet is null and whether the node status is -1, find a Node closest to the head, unpark resumes its operation, and updates the head node.
There is competition: If the lock is obtained by another thread first, enter the acquireQueued process again. If it fails, re-enter the park blocking process.
Re-entry principle:
Determine whether the current incoming thread is an occupying thread and count state+1. When releasing, first state -1 and only state=0 can be released.
Interruptible principle:
Uninterruptible mode (default). Even if it is interrupted, it will still stay in the AQS queue. After obtaining the lock, it will continue to run (the interrupt flag is set to true) to know that it has been interrupted and interrupt. Cannot be interrupted in blocking state
Interruptible mode: acquireInterruptibly() method directly throws an exception, leaves AQS, and stops waiting for the lock.
Fair lock:
During the competition, it will be checked to see if there is a predecessor node. If there is no predecessor node, it will compete.
Condition variable implementation
await process
Then enter the fullyRelease process such as AQS, release the lock on the synchronizer, directly release the reentry lock, and unpark the next node in the AQS queue to compete for the lock.
signal
First determine whether it is a thread holding the lock for signaling. If so, get the first node of the waiting queue, transfer this node to the end of the AQS queue, and convert its own status to 0, and set the previous node status to - 1
read-write lock
ReentrantReadWriteLock
When read operations are much higher than write operations, use read-write locks to allow read-read concurrency and improve performance.
Usage: Need to obtain read lock and write lock respectively
Read locks do not support condition variables
Upgrading during re-entry is not supported: Acquiring a write lock when there is a read lock will cause the acquisition of the write lock to wait forever.
It can be reduced when re-entering, first acquire the write lock, and then acquire the read lock.
Cache update strategy
Clear the cache first, then update the database
Read-write lock principle
The same Sync synchronizer is used, so the waiting queue and state are also the same.
t2 executes r.lock, and then enters the sync.acquireShared(1) process of the read lock. It will first enter the tryAcquireShared process. If there is a write lock occupied, then -1 is returned to indicate failure.
Returning 0 indicates success, but subsequent nodes will not continue to wake up.
Returning a positive integer indicates success, and the value indicates that there are still several successor nodes that need to be awakened. The read-write lock returns 1
Execute tryAcquireShared again. If successful, increase the read lock count by 1.
The wake-up of the read lock will wake up all shared threads to achieve read-read concurrency.
StampedLock
It was added in JDK8 to further optimize the reading performance. Its characteristic is that when using read locks and write locks, it must be used in conjunction with [stamp].
Optimistic reading, StampedLock supports the tryOptimisticRead() method (no lock is used in this method). After reading, a [stamp verification] needs to be done. If it passes, it means that there was no writing operation during the period and the data can be used safely. If it fails, you need to re-obtain the read lock to ensure data security.
StampedLock does not support condition variables and reentry.
Semaphore
Semaphore, used to limit the number of threads that can access shared resources at the same time
*CountdownLatch *(countdown lock)
Used for thread synchronization and cooperation, waiting for all threads to complete the countdown
The construction parameters are used to initialize the wait count value, await() is used to wait for the count to return to zero, and countDown() is used to decrement the count by one.
CyclicBarrier
Loop fence is used for thread collaboration, waiting for threads to meet a certain count. The number of counts is set during construction. When each thread executes to a certain moment that requires synchronization, the await() method is called to wait. When waiting When the number of threads reaches the count, continue execution.
The number of threads needs to be the same as the count value
It can be used repeatedly without creating a new CyclicBarrier object. After the count is used, it will be reset to the initial value. CountdownLatch is a one-time use.
Top comments (0)