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

What is AbstractQueuedSynchronizer?

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

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

This article mainly introduces "what is AbstractQueuedSynchronizer". In daily operation, I believe many people have doubts about what is AbstractQueuedSynchronizer. The editor consulted all kinds of materials and sorted out simple and easy-to-use methods of operation. I hope it will be helpful for you to answer the doubts about "what is AbstractQueuedSynchronizer?" Next, please follow the editor to study!

Deep and simple AbstractQueuedSynchronizer

Reentrant locks (ReentrantLock) and semaphores (Semaphore) are two very important concurrency control tools in Java multithreaded programming. I believe that most readers should be familiar with their use (if you are not clear about the little friends, quickly take out the book and read it).

But do you know the implementation details of overloaded locks and semaphores? Let me show you how they are realized.

First of all, let's start with an important class diagram to illustrate the relationship between the three:

As you can see, the reentrant lock and semaphore are internal, implementing a subclass of AbstractQueuedSynchronizer whose name is Sync. This Sync class is also the core implementation of reentrant locks and semaphores. The code in the subclass Sync is also relatively small, and its core algorithms are provided by AbstractQueuedSynchronizer. Therefore, it can be said that as long as you understand AbstractQueuedSynchronizer, you will clearly know the principle of the implementation of reentrant locks and semaphores.

What you need to know about AbstractQueuedSynchronizer

Before entering AbstractQueuedSynchronizer, there are still some basics to understand, so that we can better understand the implementation of AbstractQueuedSynchronizer.

License-based multithreading control

In order to control multiple threads to access shared resources, we need to assign a license to each thread accessing the shared interval. A thread that gets a license can enter the shared zone activity. When the thread finishes its work and leaves the shared zone, the license must be returned to ensure that subsequent threads can obtain the license normally. If the license runs out, the thread must wait when it enters the shared interval, which is the basic idea of controlling multithreaded parallelism.

For example, a large group of children go to the amusement park to play the Ferris wheel, and only 20 children can sit on the Ferris wheel. But there were 100 children here. Then the number of permissible is 20. In other words, only 20 children can play on the Ferris wheel at a time, and other children have to wait in line. Only when the children on the Ferris wheel leave the control position can other children go up and play.

Therefore, using permission to control thread behavior is almost the same as queuing up to play the Ferris wheel.

Exclusive lock and shared lock

The second important concepts are exclusive and shared. As the name implies, in exclusive mode, only one thread can access shared variables, while shared mode allows multiple threads to access it at the same time. Simply put, the reentrant lock is exclusive; the semaphore is shared.

In the words of the Ferris wheel, the exclusive lock means that although I have 20 seats here, the children can only go on one by one. What about the extra seats? they can be empty, or they can let the only child on the Ferris wheel change. It doesn't matter where he wants to sit and change seats in one minute. The shared lock is the normal way to play with the Ferris wheel.

LockSupport

LockSupport can be understood as a utility class. Its simple function is to suspend and resume execution of the thread. Its common API is as follows:

Public static void park (): suspends the current thread if no license is available

Public static void unpark (Thread thread): give thread an available license to continue execution

Because the word park means to stop, the park () function here means to pause the thread. Unpark (), on the other hand, means that the thread continues to execute.

It should be noted that LockSupport itself is also a license-based implementation, so how to understand this sentence, please see the following code:

LockSupport.unpark (Thread.currentThread ()); LockSupport.park ()

You can guess, after park (), whether the current thread will stop or can continue to execute?

The answer is: you can continue to execute. That's because unpark () is executed before park (), which releases a license, which means that the current thread has a license available. Park (), on the other hand, does not block threads if permission is available.

To sum up, the execution effect of park () and unpark () has nothing to do with the order in which it is called. This is important because in a multi-threaded environment, it is often difficult to ensure the sequence of function calls (all executed concurrently in different threads), so this license-based approach ensures that the program is error-free as much as possible.

Compared with park () and unpark (), a typical negative example is Thread.resume () and Thread.suspend ().

Look at the following code:

Thread.currentThread (). Resume (); Thread.currentThread (). Suspend ()

First let the thread continue to execute, and then suspend the thread. This is very similar to the example of park () above, but the result is quite different. Here, the current thread is stuck.

Therefore, using park () and unpark () is our first choice. In AbstractQueuedSynchronizer, it is the park () and unpark () operations of LockSupport that control the running state of the thread.

AbstractQueuedSynchronizer internal data structure

All right, that's all for the basics. Next, let's get to the point: first take a look at the internal data structure of AbstractQueuedSynchronizer.

Inside AbstractQueuedSynchronizer, there is a queue, which we call a synchronous wait queue. Its purpose is to hold the thread waiting on the lock (the wait caused by the lock () operation). In addition, in order to maintain waiting threads waiting on conditional variables, AbstractQueuedSynchronizer needs to maintain another conditional variable waiting queue, that is, threads that are blocked by Condition.await ().

Because a reentrant lock can generate multiple conditional variable objects, a reentrant lock may have multiple conditional variables waiting for the queue. In fact, a waiting list is maintained inside each condition variable object. Its logical structure is as follows:

The following class diagram shows the specific implementation at the code level:

As you can see, both the synchronous wait queue and the condition variable wait queue use the same Node class as the node of the linked list. For synchronous wait queues, the Node includes the previous element prev of the linked list, the next element next, and the thread object thread. For the condition variable wait queue, nextWaiter is also used to represent the next node waiting in the condition variable queue.

Another important member of the Node node is waitStatus, which represents the state that the node is waiting in the queue:

CANCELLED: indicates that the thread canceled the wait. If some exception occurs during the acquisition of the lock, a cancellation may occur, such as an interrupt exception or a timeout while waiting.

SIGNAL: indicates that subsequent nodes need to be awakened.

CONDITION: the thread waits in the queue of conditional variables.

PROPAGATE: propagates releaseShared status unconditionally in shared mode. Early JDK did not have this state, which, at first glance, was superfluous. This state was introduced to address bug 6801020, which causes threads to hang due to the concurrent release of shared locks. (with the continuous improvement of JDK, its code becomes more and more difficult to understand. (just like our own engineering code, the details become more and more obscure as we fix more bug.)

0: initial statu

Among them CANCELLED=1,SIGNAL=-1,CONDITION=-2,PROPAGATE=-3. In the specific implementation, you can simply determine whether it is a CANCELLED state by releasing waitStatus less than or equal to 0.

Exclusive lock

Now that you understand the basic implementation ideas and data structures of AbstractQueuedSynchronizer, let's take a look at its implementation details. First, let's take a look at the implementation of an exclusive lock. Reentrant lock is a typical exclusive lock.

Request lock

The following is the code for requesting permission for an exclusive lock:

Public final void acquire (int arg) {/ / attempts to obtain a license, arg is the number of licenses. For reentrant locks, one at a time. If (! tryAcquire (arg) & & / / if tryAcquire fails, first use addWaiter () to queue the current thread for synchronization / / and then continue to try to acquire the lock acquireQueued (addWaiter (Node.EXCLUSIVE), arg) selfInterrupt ();}

Take a look at the tryAcquire () function. The purpose of this function is to try to get a license. For AbstractQueuedSynchronizer, this is an unimplemented abstract function.

The specific implementation is in the subclass. In the implementation of reentrant lock, read-write lock, semaphore and so on, they all have their own implementation.

If tryAcquire () succeeds, acquire () returns success directly. If it fails, the current thread is added to the synchronous waiting queue with addWaiter ().

Next, using the acquireQueued () function to request a lock for a thread that is already in the queue, you can see from the function name that its parameter node must be a node that is already waiting in the queue. Its function is to request a license for the Node that is already in the queue.

You should take a good look at this function, because both the normal lock () method and the await () of the condition variable will use this method.

Conditional variable waiting

If Condition.await () is called, then the thread will also wait, so let's look at the implementation:

Signal () notification of the Condition object

When signal () notifies, it is in the conditional waiting queue, according to FIFO, starting with the first node:

Release () releases the lock

Releasing the exclusive lock is simple.

Public final boolean release (int arg) {/ / tryRelease () is an abstract method that has a concrete implementation in the subclass like tryAcquire () if (tryRelease (arg)) {Node h = head; if (h! = null & & h.waitStatus! = 0) / / wakes up a waiting thread from the queue (directly skipped by CANCEL) unparkSuccessor (h) Return true;} return false;} shared lock

The implementation of a shared lock is slightly more complex than an exclusive lock. It's easy to understand. Because the scene of exclusive lock is very simple, single in and out, but shared lock is different. It may be N in and M out, which is more troublesome to deal with. However, their core ideas are still the same. Several typical applications of shared locks are: semaphores, write locks in read-write locks.

Get a shared lock

In order to implement shared locks, there is a special method for shared locks in AbstractQueuedSynchronizer.

To get a shared lock, use the acquireShared () method:

Release the shared lock

The code to release the shared lock is as follows:

Public final boolean releaseShared (int arg) {/ / tryReleaseShared () attempts to release the license, which is an abstract method that needs to implement this function in the subclass if (tryReleaseShared (arg)) {/ / the above code, that is, wake up the thread and set the propagation state doReleaseShared (); return true;} return false At this point, the study of "what is AbstractQueuedSynchronizer" is over. I hope I can solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

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

Development

Wechat

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

12
Report