In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-03 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
In this issue, the editor will bring you the analysis of the read-write lock and its implementation in the Java concurrent package. The article is rich in content and analyzes and narrates it from a professional point of view. I hope you can get something after reading this article.
1. Preface
The locks commonly used in Java concurrent packages (such as ReentrantLock) are basically exclusive locks, which only allow one thread to access at the same time, while read-write locks allow multiple reading threads to access at the same time, but when writing threads access, all reading threads and other writing threads are blocked. Read-write locks maintain a pair of locks, a read lock and a write lock, and the concurrency is greatly improved compared with the general exclusive lock by separating the read lock and the write lock.
In addition to ensuring the improvement of the visibility and concurrency of write operations to read operations, read-write locks can simplify the programming of read-write interaction scenarios. Suppose that a shared data structure is defined in the program as a cache, which provides read services (such as queries and searches) most of the time, while writes take up very little time, but updates after the completion of the write operation need to be visible to subsequent read services.
When there is no read-write lock support (before Java 5), if you need to complete the above work, you should use Java's waiting notification mechanism, that is, when the write operation starts, all read operations later than the write operation will enter the waiting state, and only after the write operation is completed and notified can all waiting read operations continue (synchronization between writes depends on the synchronized keyword) The purpose of this is to enable all read operations to read the correct data without dirty reads. To use a read-write lock to achieve the above functions, you only need to obtain the read lock during the read operation and the write lock during the write operation. When the write lock is acquired, subsequent read and write operations (not the current write operation thread) will be blocked. After the write lock is released, all operations continue to be executed, and the programming method becomes simple and straightforward compared with the implementation of waiting for notification mechanism.
In general, read-write locks perform better than exclusive locks, because most scenarios read more than write. In the case of more reads than writes, read-write locks provide better concurrency and throughput than exclusive locks. The implementation of the Java concurrent package that provides read-write locks is ReentrantReadWriteLock, which provides features as shown in Table 1.
Table 1. Characteristics of ReentrantReadWriteLock
Characteristics
Description
Fair choice
Support unfair (default) and fair lock acquisition, throughput is still unfair better than fair
Re-entry
The lock supports reentry. Take the reader thread as an example: after acquiring the read lock, the reader thread can acquire the read lock again. After acquiring the write lock, the writer thread can acquire the write lock again and the read lock at the same time.
Lock degradation
Following the order of acquiring a write lock, acquiring a read lock and then releasing a write lock, a write lock can be degraded to a read lock.
two。 Interface and example of read-write lock
ReadWriteLock only defines two methods to acquire read lock and write lock, that is, readLock () and writeLock () method, and its implementation-ReentrantReadWriteLock, in addition to interface methods, also provides some methods to facilitate the outside world to monitor its internal working state, these methods and descriptions are shown in Table 2.
Table 2. Ways for ReentrantReadWriteLock to show internal working status
Method name
Description
Int getReadLockCount ()
Returns the number of times the current read lock was acquired. This number is not equal to the number of threads acquiring the read lock, for example, if only one thread acquires (re-enters) the read lock for n times in a row, then the number of threads occupying the read lock is 1, but the method returns n
Int getReadHoldCount ()
Returns the number of times the current thread acquired the read lock. This method is added to ReentrantReadWriteLock in Java 6, and ThreadLocal is used to save the number of times obtained by the current thread, which also makes the implementation of Java 6 more complex.
Boolean isWriteLocked ()
Determine whether the write lock is acquired
Int getWriteHoldCount ()
Returns the number of times the current write lock has been acquired
Next, a caching example is used to illustrate the use of read-write locks, as shown in listing 1.
Listing 1. Cache.java
Public class Cache {static Map map = new HashMap (); static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock (); static Lock r = rwl.readLock (); static Lock w = rwl.writeLock (); / / get a key corresponding to value public static final Object get (String key) {r.lock (); try {return map.get (key);} finally {r.unlock () }} / / set the value corresponding to key, and return the old value public static final Object put (String key, Object value) {w.lock (); try {return map.put (key, value);} finally {w.unlock ();} / / clear all content public static final void clear () {w.lock (); try {map.clear () } finally {w.unlock ();}
In the above example, Cache combines a non-thread-safe HashMap as a cache implementation, while using both read and write locks to ensure that Cache is thread-safe. In the read operation get (String key) method, the read lock needs to be acquired so that concurrent access to the method is not blocked. The write operation put (String key, Object value) and clear () methods must acquire the write lock in advance when updating the HashMap. When the write lock is acquired, the acquisition of the read lock and write lock by other threads is blocked, and other read and write operations can continue only after the write lock is released. Cache uses read-write locks to improve read concurrency, ensures the visibility of each write to all read and write operations, and simplifies programming.
3. Analysis on the implementation of read-write Lock
Next, we will analyze the implementation of ReentrantReadWriteLock, including the design of read-write state, the acquisition and release of write lock, the acquisition and release of read lock and the degradation of lock (the following does not specify that read-write lock can be regarded as ReentrantReadWriteLock).
3.1 Design of read and write status
Read-write locks also rely on custom synchronizers to achieve synchronization, and the read-write state is the synchronization state of their synchronizers. Recalling the implementation of the custom synchronizer in ReentrantLock, the synchronization state represents the number of times the lock is repeatedly acquired by a thread, while the custom synchronizer of the read-write lock needs to maintain the state of multiple readers and one write thread on the synchronization state (an integer variable), which makes the design of this state the key to the implementation of the read-write lock.
If you maintain multiple states on an integer variable, you must use the variable "cut by bit". The read-write lock divides the variable into two parts, with the high 16 bits for reading and the low 16 bits for writing, as shown in figure 1.
Figure 1. The division of read-write lock state
As shown in figure 1, the current synchronization state indicates that a thread has acquired the write lock and re-entered it twice, while also acquiring the read lock twice in a row. How can read-write locks quickly determine the status of read and write respectively? The answer is through bit operations. Suppose the current synchronization state value is S, the write state is equal to S & 0x0000FFFF (all the high 16 bits are erased), and the read state is equal to S > 16 (unsigned complement 0 moves 16 bits to the right). When the write state increases by 1, it equals S + 1, and when the read state increases by 1, S + (1 > > 16) is greater than 0, that is, the read lock has been acquired.
3.2 acquisition and release of write locks
A write lock is an exclusive lock that supports reentry. If the current thread has acquired the write lock, increase the write state. If the read lock has been acquired by the current thread when it acquires the write lock (the read state is not 0) or if the thread is not the thread that has acquired the write lock, the current thread enters a waiting state and the code for acquiring the write lock is shown in listing 2.
Listing 2. TryAcquire method of ReentrantReadWriteLock
Protected final boolean tryAcquire (int acquires) {Thread current = Thread.currentThread (); int c = getState (); int w = exclusiveCount (c); if (c! = 0) {/ / there is a read lock or the current acquiring thread is not a thread that has acquired a write lock (w = 0 | | current! = getExclusiveOwnerThread ()) return false; if (w + exclusiveCount (acquires) > MAX_COUNT) throw new Error ("Maximum lock count exceeded") SetState (c + acquires); return true;} if (writerShouldBlock () | |! compareAndSetState (c, c + acquires)) {return false;} setExclusiveOwnerThread (current); return true;}
This method adds a judgment of the existence of a read lock in addition to the reentrant condition (the current thread is the thread that acquired the write lock). If there is a read lock, the write lock cannot be acquired because the read-write lock ensures that the operation of the write lock is visible to the read lock, and if the read lock is allowed to acquire the write lock if it has been acquired, then other running readers are not aware of the operation of the current writer thread. Therefore, only by waiting for the other reader threads to release the read lock, the write lock can be acquired by the current thread, and once the write lock is acquired, the subsequent access of other read and write threads is blocked.
The release process of the write lock is basically similar to that of the ReentrantLock. Each release reduces the write state. When the write state is 0, the write lock has been released, so that the waiting read-write thread can continue to access the read-write lock, and the modification of the previous write thread is visible to the subsequent read-write thread.
3.3 acquisition and release of read locks
A read lock is a shared lock that supports reentry, which can be acquired by multiple threads at the same time, and the read lock will always be acquired successfully when there is no other write thread access (or the write state is 0). All you have to do is (thread-safe) to increase the read state. If the current thread has acquired the read lock, increase the read state. If the write lock has been acquired by another thread when the current thread acquires the read lock, it enters a waiting state. The implementation of acquiring a read lock from Java 5 to Java 6 becomes much more complex, mainly due to the addition of some features, such as the getReadHoldCount () method, which returns the number of times the current thread acquired the read lock. The read state is the sum of the number of times that all threads acquire the read lock, and the number of times each thread acquires the read lock can only be saved in ThreadLocal and maintained by the thread itself, which makes the implementation of acquiring read lock more complicated. Therefore, here you delete the code that acquires the read lock, leaving the necessary parts, as shown in listing 3.
Listing 3. TryAcquireShared method of ReentrantReadWriteLock
Protected final int tryAcquireShared (int unused) {for (;;) {int c = getState (); int nextc = c + (1)
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.