Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to use ReentrantLock of JUC

2025-03-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly explains "how to use ReentrantLock in JUC". The content in the article is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "how to use ReentrantLock in JUC".

ReentrantLock is translated as a reentrant lock, and we always compare it with the synchronized keyword when using it. In fact, the ReentrantLock keyword and the synchronized keyword have the same semantics, except that ReentrantLock is more operable than the synchronized keyword for developers, so it is more flexible to use. Of course, there are two sides to everything, and there is a risk of more error-prone behind the flexibility.

Despite the same semantics, the implementation mechanisms behind the ReentrantLock and synchronized keywords are quite different. In the previous article, we analyzed the implementation of the synchronized keyword and learned that the synchronized keyword depends on monitor technology, while the ReentrantLock to be analyzed in this article depends on the AQS queue synchronizer. Let's find out how to implement it based on AQS.

ReentrantLock example

This section uses ReentrantLock to implement a program that prints alternately with 3 threads, demonstrating the acquisition and release of locks and the notification mechanism between threads based on ReentrantLock. The example implementation is as follows:

Private static Lock lock = new ReentrantLock (true); private static Condition ca = lock.newCondition (); private static Condition cb = lock.newCondition (); private static Condition cc = lock.newCondition (); private static volatile int idx = 0politics private static class An implements Runnable {@ Override public void run () {try {lock.lock (); for (int I = 0; I)

< 10; i++) { cb.signalAll(); System.out.println("a: " + (++idx)); ca.await(); } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }}private static class B implements Runnable { @Override public void run() { try { lock.lock(); for (int i = 0; i < 10; i++) { cc.signalAll(); System.out.println("b: " + (++idx)); cb.await(); } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }}private static class C implements Runnable { @Override public void run() { try { lock.lock(); for (int i = 0; i < 10; i++) { ca.signalAll(); System.out.println("c: " + (++idx)); cc.await(); } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }}public static void main(String[] args) { new Thread(new A()).start(); new Thread(new B()).start(); new Thread(new C()).start();} 上述示例定义了 3 个线程类 A、B 和 C,并按照 A ->

Organized in the order of B-> C, each thread will first attempt to notify the successor thread (move the corresponding thread to the synchronization queue) after calling the Lock#lock method to acquire the lock, then accumulate and print the idx variable, then enter the waiting state and release resources, and the method Lock#unlock will then schedule the thread at the head node of the synchronous queue to continue execution.

Implementing Insider Lock Interface with ReentrantLock

ReentrantLock implements the Lock interface, which abstracts the basic operations of a lock, including the acquisition and release of lock resources, and the creation of conditional objects. In addition to the ReentrantLock introduced in this article, the components in JUC that directly or indirectly implement the Lock interface include ReentrantReadWriteLock and StampedLock, which we will examine one by one in a later article. The definition of Lock interface is as follows:

Public interface Lock {void lock (); void lockInterruptibly () throws InterruptedException; boolean tryLock (); boolean tryLock (long time, TimeUnit unit) throws InterruptedException; void unlock (); Condition newCondition ();}

Each method is defined as follows:

Lock (): gets the lock resource and blocks if it fails.

LockInterruptibly (): gets the lock resource, blocks if it fails, and supports responding to interrupt requests during blocking.

TryLock (): attempts to get the lock resource, returns immediately regardless of whether it is successful or not, returns true if it is successful, returns false otherwise.

TryLock (long time, TimeUnit unit): attempts to acquire lock resources, introduces a timeout mechanism relative to the no-parameter version of the tryLock method, and supports responding to interrupt requests while waiting.

Unlock (): releases lock resources.

NewCondition (): creates a conditional object bound to the current Lock.

Acquisition and release of resources

The previous section analyzed the definition of the Lock interface, which was implemented by ReentrantLock, and delegated the implementation of the interface methods to the Sync inner class processing. Sync is an abstract class that inherits from AbstractQueuedSynchronizer and derives two subclasses, FairSync and NonfairSync (the inheritance relationship is shown below). From the naming, we can see that FairSync implements fair locking, while NonfairSync implements unfair locking.

ReentrantLock provides a construction method with a boolean parameter to determine whether to create a fair lock or an unfair lock (the default is an unfair lock). The construction method is defined as follows:

Public ReentrantLock () {/ / create an unfair lock by default sync = new NonfairSync ();} public ReentrantLock (boolean fair) {/ / create a fair lock or an unfair lock based on parameters sync = fair? New FairSync (): new NonfairSync ();}

The following will distinguish between fair locks and unfair locks and analyze the specific implementation of ReentrantLock for the Lock interface method. Before we begin, we will introduce the role of the state field in AQS in ReentrantLock.

We know that ReentrantLock is reentrant, and reentrant here means that when a thread acquires a ReentrantLock lock, it can still succeed if the thread tries to acquire the ReentrantLock lock again, and the corresponding number of reentrants is increased by 1. The number of reentrants of ReentrantLock is recorded by the state field of AQS. When state is 0, the target ReentrantLock lock is not currently held by any thread, and when a thread releases the ReentrantLock lock, the corresponding state value needs to be reduced by 1.

Unfair lock

In this section, we analyze the implementation mechanism of the unfair lock NonfairSync. First, let's take a look at the NonfairSync#lock method, which is used to obtain resources. If the acquisition fails, the current thread is added to the synchronization queue to block waiting. The method is implemented as follows:

Final void lock () {/ / attempt to acquire the lock, set state from 0 to 1 if (this.compareAndSetState (0,1)) {/ / acquire the lock successfully for the first time, and record the current lock object this.setExclusiveOwnerThread (Thread.currentThread ());} else {/ / the target lock object is already occupied, or the target lock object this.acquire (1) is not acquired for the first time;}}

Method the process of NonfairSync#lock locking will first try to change the state value of ReentrantLock from 0 to 1 based on the CAS operation to preempt lock resources, which is also the root of unfair semantics. If the operation is successful, the target ReentrantLock lock is not currently held by any thread, and the lock is successful this time. If the operation fails, distinguish between two situations:

The target ReentrantLock lock is already held by the current thread.

The target ReentrantLock lock is already held by another thread.

For both cases, the next step is to call the AbstractQueuedSynchronizer#acquire method to try to get 1 unit of resources, which is implemented by AQS, which we have analyzed in the previous article, where the template method AbstractQueuedSynchronizer#tryAcquire is executed. NonfairSync implements the template method as follows:

Protected final boolean tryAcquire (int acquires) {return this.nonfairTryAcquire (acquires);}

The above method delegates the logic of the attempt to get the resource to the Sync#nonfairTryAcquire method execution, and the ReentrantLock#tryLock () method of ReentrantLock is also implemented based on this method. Let's analyze the execution logic of this method, and the implementation is as follows:

Final boolean nonfairTryAcquire (int acquires) {/ / get the current thread object final Thread current = Thread.currentThread (); / / get the state value int c = this.getState (); if (c = = 0) {/ / state is 0, indicating that the target lock is not currently held. Try to acquire the lock if (this.compareAndSetState (0, acquires)) {this.setExclusiveOwnerThread (current); return true }} / / if the thread that already holds the lock is already the current thread else if (current = = this.getExclusiveOwnerThread ()) {/ / number of reentrants plus 1 int nextc = c + acquires; if (nextc < 0) {/ / number of reentrants overflow throw new Error ("Maximum lock count exceeded") } / / update the number of reentrants of the state record this.setState (nextc); return true;} / / the thread that already holds the lock is not the current thread, attempt to lock failed return false;}

The execution process of method Sync#nonfairTryAcquire can be summarized as follows

Gets the state value of the current ReentrantLock lock

If the state value is 0, the current ReentrantLock lock is not held by any thread. Based on the CAS attempt to change the state value from 0 to 1, the lock resource is preempted. If the lock is modified successfully, the lock is successfully added.

Otherwise, if the thread that currently holds the ReentrantLock lock is itself, modify the number of reentrants (that is, increase the state value by 1)

Otherwise, the target ReentrantLock lock is already held by another thread and the lock fails.

If the Sync#nonfairTryAcquire method returns false, the current thread fails in its attempt to acquire the target ReentrantLock lock. For the ReentrantLock#lock method, the thread is then added to the synchronization queue to block waiting, while for the ReentrantLock#tryLock () method, the thread exits immediately and returns false.

The method ReentrantLock#newCondition is also delegated to the Sync#newCondition method, which simply creates a ConditionObject object, that is, a new conditional queue. The following methods in the unfair lock NonfairSync are directly delegated to AQS processing, and the implementation mechanisms of these methods have been introduced in the previous analysis of AQS:

ReentrantLock#lockInterruptibly: directly delegated to the AbstractQueuedSynchronizer#acquireInterruptibly method implementation, the number of resources obtained is 1.

ReentrantLock#tryLock (long, java.util.concurrent.TimeUnit): directly delegated to the AbstractQueuedSynchronizer#tryAcquireNanos method implementation, the number of resources obtained is 1.

ReentrantLock#unlock: directly delegated to the AbstractQueuedSynchronizer#release method implementation, the number of resources released is 1.

In the previous article, when we analyzed the AbstractQueuedSynchronizer#release method of AQS, we described that this method calls the template method AbstractQueuedSynchronizer#tryRelease to try to release resources. ReentrantLock's implementation of this template method is located in the Sync abstract class, so it is a method shared by NonfairSync and FairSync, so let's analyze the implementation of this method.

Protected final boolean tryRelease (int releases) {/ / subtracts the number of reentrants recorded by the current state by 1 int c = this.getState ()-releases; / / throws an exception if (Thread.currentThread ()! = this.getExclusiveOwnerThread ()) {throw new IllegalMonitorStateException ();} boolean free = false if the thread object currently holding the lock is not the current thread. / / if the number of reentrants has been reduced to 0, clear the thread object if (c = = 0) {free = true; this.setExclusiveOwnerThread (null);} / / update the number of reentrants of the current lock this.setState (c); return free;}

The process of trying to release resources is essentially the process of modifying the value of the state field. If the thread currently operating is a thread holding a ReentrantLock lock, the above method will subtract the state value by 1, that is, the number of reentrants by 1. If the modified state field value is 0, the current thread has released the held ReentrantLock lock, and the thread Thread object recorded in the ReentrantLock object needs to be cleared.

Fair lock

In this section, we analyze the implementation mechanism of fair lock FairSync. In essence, fairness refers to the fair acquisition of lock resources, so the main difference is reflected in the locking process, that is, the ReentrantLock#lock method.

We saw earlier when analyzing NonfairSync that NonfairSync will first try to change the state value from 0 to 1 based on CAS when adding locks. In case of failure, it will continue to call the AbstractQueuedSynchronizer#acquire method to wait for resources to be obtained, and will still preempt lock resources when state is 0 while waiting in the synchronization queue.

The difference between FairSync and NonfairSync is that when the state value is 0, that is, when the target ReentrantLock lock is not held by any thread at this time, FairSync does not preempt the lock resources, but checks whether there are other threads in the synchronization queue waiting to acquire the lock resources, and if so, give priority to these threads to obtain the lock resources.

Let's take a look at the implementation of the FairSync#lock method, which simply delegates the operation of acquiring lock resources to the AbstractQueuedSynchronizer#acquire method of AQS, so we need to focus on the implementation of the template method FairSync#tryAcquire:

Protected final boolean tryAcquire (int acquires) {/ / get the current thread object final Thread current = Thread.currentThread (); / / get the current state value int c = this.getState () If (c = = 0) {/ / state is 0, which means that the target lock is not currently held. Check whether there is a blocking thread waiting for the current lock. If you do not attempt to acquire the lock if (! this.hasQueuedPredecessors () & & this.compareAndSetState (0, acquires)) {this.setExclusiveOwnerThread (current); return true }} / / if the thread that already holds the lock is already the current thread, modify the number of reentrants plus 1 else if (current = = this.getExclusiveOwnerThread ()) {int nextc = c + acquires; if (nextc < 0) {throw new Error ("Maximum lock count exceeded");} this.setState (nextc) Return true;} return false;}}

The execution process of the above method is more or less the same as the related implementation in NonfairSync. The main difference is that when the state value is 0, FairSync will call AbstractQueuedSynchronizer#hasQueuedPredecessors to check whether there are other threads in the current synchronization queue waiting for lock resources. If so, let these threads obtain lock resources first, and queue themselves in the synchronization queue.

Thank you for your reading, the above is the content of "how to use JUC ReentrantLock", after the study of this article, I believe you have a deeper understanding of how to use JUC ReentrantLock, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

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.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report