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

Source code analysis of CountDownLatch and Atomic atomic operation class

2025-03-27 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

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

This article focuses on "CountDownLatch and Atomic atomic operation class source code analysis", interested friends may wish to have a look. The method introduced in this paper is simple, fast and practical. Next let the editor to take you to learn "CountDownLatch and Atomic atomic operation class source code analysis" it!

Introduction language

In this section, let's take a look at the atomic operation classes that start with CountDownLatch and Atomic. The source code of CountDownLatch is very little, and it looks simple, but the practical application of CountDownLatch is not very easy; the atomic operation class of Atomic is easier to understand and apply, so let's take a look at it respectively.

1 、 CountDownLatch

CountDownLatch Chinese is sometimes called a counter, and it is also translated as a count lock. Its biggest function is not to add a lock, but to achieve the function of waiting through counting. There are mainly two forms of waiting:

Let a group of threads execute together after all starts (the first started thread needs to block the waiting thread until all the set of threads have been started, and then execute together)

The main thread waits for another set of threads to finish execution before continuing execution.

We will give an example to demonstrate these two situations, but before we do that, let's take a look at the underlying source code implementation of CountDownLatch to make it clearer, otherwise we will look at the example in the first place, which may be difficult to understand.

CountDownLatch has two more important API, await and countDown, which manage whether threads can acquire locks and lock release (also known as the increase and decrease of state counts).

1.1 、 await

Await can be called waiting or locking. There are two different ways to input parameters. The source code is as follows:

Public void await () throws InterruptedException {sync.acquireSharedInterruptibly (1);} / / with a timeout, will eventually be converted to millisecond public boolean await (long timeout, TimeUnit unit) throws InterruptedException {return sync.tryAcquireSharedNanos (1, unit.toNanos (timeout));}

At the bottom of both methods, sync,sync is a synchronizer, implemented by the inner class of CountDownLatch, as follows:

Private static final class Sync extends AbstractQueuedSynchronizer {}

You can see that Sync inherits AbstractQueuedSynchronizer and has the general functionality of a synchronizer.

The underlying layer of non-parameter await uses the acquireSharedInterruptibly method, and the parameters use the tryAcquireSharedNanos method. Both of these methods are AQS methods. The underlying implementation is very similar, which is mainly divided into two steps:

1. Use the tryAcquireShared method of the subclass to attempt to acquire the lock. If the lock is acquired and returned directly, the lock will not be obtained.

two。 If you can't get the lock, encapsulate the current thread with Node, append it to the end of the synchronization queue, and wait for the lock to be acquired at the right time.

The second step is that AQS has been implemented. In the first step, the tryAcquireShared method is given to Sync for implementation. The source code is as follows:

/ / if the status of the current synchronizer is 0, the lock protected int tryAcquireShared (int acquires) {return (getState () = = 0)? 1:-1;} is available.

The code to acquire the lock is also simple, judging directly from the state field of the synchronizer, but there are still two points to note:

When the lock is acquired, the value of state will not change, for example, when ReentrantLock acquires the lock, it will put state + 1, but CountDownLatch will not

The state of CountDownLatch is not the default value of AQS, but can be assigned. It is assigned when CountDownLatch is initialized.

The code is as follows:

/ / initialization, count represents the initialization value of state public CountDownLatch (int count) {if (count < 0) throw new IllegalArgumentException ("count < 0"); / / the underlying code of new Sync is state = count; this.sync = new Sync (count);}

The meaning of the initialized count here is different from that of the general lock. Count indicates the number of threads we want to wait. In two different waiting scenarios, count has different meanings:

In the wait scenario where a group of threads are started and then executed together, count represents the number of threads in a group.

In the wait scenario in which the main thread waits for another set of threads to finish execution before continuing execution, count represents the number of threads in a group.

So we can think of count as the number of threads we want to wait for, maybe we are waiting for a set of threads to start to complete, maybe we are waiting for a group of threads to complete execution.

1.2 、 countDown

The Chinese translation of countDown is a countdown. Each call will reduce the state by one. The underlying method is called as follows:

Public void countDown () {sync.releaseShared (1);}

ReleaseShared is a method defined by AQS, which is divided into two steps:

1. Try to release the lock (tryReleaseShared). If the lock is not released, it will return directly. If the lock is released successfully, go 2.

two。 Releases the post-waiting node of the current node.

The second step AQS has been implemented, and the first step is implemented by Sync. Let's take a look at the implementation source code of the tryReleaseShared method:

/ / A pair of state is decremented until state becomes 0 / / when state decrements to 0, return true, and the rest return falseprotected boolean tryReleaseShared (int releases) {/ / spin guarantee CAS will be successful for (;;) {int c = getState (); / / state is already 0, return false if (c = = 0) return false; / / A pair of state is decremented int nextc = CMu1 If (compareAndSetState (c, nextc)) return nextc = = 0;}}

As you can see from the source code, countDown will not return true until count decrements to 0.

1.3, example

After reading the two important API of CountDownLatch, let's implement the two functions mentioned at the beginning of the article:

Let a group of threads execute together after they have all been started

The main thread waits for another set of threads to finish execution before continuing execution.

The code is in the CountDownLatchDemo class. You can debug it. The source code is as follows:

Public class CountDownLatchDemo {/ / Thread task class Worker implements Runnable {/ / define the count lock to implement the function 1 private final CountDownLatch startSignal; / / define the count lock to implement function 2 private final CountDownLatch doneSignal; Worker (CountDownLatch startSignal, CountDownLatch doneSignal) {this.startSignal = startSignal; this.doneSignal = doneSignal } / / things done by child threads public void run () {try {System.out.println (Thread.currentThread (). GetName () + "begin") There are two points to note when await: the state will not change during await, and the state initialization of 2:startSignal is 1, so all child threads cannot acquire the lock and need to wait in the synchronization queue, so that the first started child thread waits for the result of the later started child thread startSignal.await (); doWork () / / countDown will subtract state by one each time, and doneSignal will return false (releaseShared method) for the first 8 executions of the doneSignal. When the state is decremented to 0 for the ninth execution, the countDown will succeed, indicating that all child threads have finished executing, and the main thread doneSignal.countDown () of await on the doneSignal will be released; System.out.println (Thread.currentThread (). GetName () + "end") } catch (InterruptedException ex) {} / / return;} void doWork () throws InterruptedException {System.out.println (Thread.currentThread (). GetName () + "sleep 5s.") ; Thread.sleep (5000l) } @ Test public void test () throws InterruptedException {/ / state initialization to 1 is critical, the state will not change when the child thread is continuous await,await, and it is found that the state is 1, all threads cannot acquire the lock / / cause all threads to wait in the synchronization queue, when the main thread executes countDown, the waiting thread will be released together CountDownLatch startSignal = new CountDownLatch (1) / / state initialized to 9, indicating that the main thread CountDownLatch doneSignal = new CountDownLatch (9); for (int I = 0; I < 9; + + I) / / create and start threads {new Thread (new Worker (startSignal, doneSignal)). Start ();} System.out.println ("main thread begin") / / this line of code wakes up nine child threads to begin execution (because the state of the startSignal lock is 1, nine waiting child threads can be released with one call to the countDown method) startSignal.countDown (); / / this line of code causes the main thread to fall into a deep sleep and wait for the nine child threads to finish execution before continuing execution (that is, waiting for the child thread to execute doneSignal.countDown ()) doneSignal.await () System.out.println ("main thread end");}} execution result: Thread-0 beginThread-1 beginThread-2 beginThread-3 beginThread-4 beginThread-5 beginThread-6 beginThread-7 beginThread-8 beginmain thread beginThread-0sleep 5s. Thread-1sleep 5s. Thread-4sleep 5s. Thread-3sleep 5s. Thread-2sleep 5s. Thread-8sleep 5s. Thread-7sleep 5s. Thread-6sleep 5s. Thread-5sleep 5s. Thread-0 endThread-1 endThread-4 endThread-3 endThread-2 endThread-8 endThread-7 endThread-6 endThread-5 endmain thread end

From the implementation results, we can see that the above two functions have been achieved, and the realization is compared. You can take a look at it according to the comments and debug.

2. Atomic atomic operation class

There are many atomic operation classes that start with Atomic, and there are basically corresponding Atomic atomic operation classes related to the number types commonly used in Java, as shown in the following figure:

The atomic operation classes that start with Atomic are thread-safe in high concurrency scenarios and we can rest assured to use them.

Let's take AtomicInteger as an example to look at the main underlying implementations:

Private volatile int value;// initializes public AtomicInteger (int initialValue) {value = initialValue;} / / gets the current value public final int get () {return value;} / / self-increment 1, and returns the previous value public final int getAndIncrement () {return unsafe.getAndAddInt (this, valueOffset, 1);} / minus 1, and returns the previous value public final int getAndDecrement () {return unsafe.getAndAddInt (this, valueOffset,-1);}

From the source code, we can see that the thread-safe operation methods are all implemented using unsafe methods at the bottom, and the above unsafe methods are all thread-safe, not using Java.

AtomicInteger is to increase and decrease the value of the int type, so what if the object of Atomic is a custom class? Java also provides an atomic operation class for custom objects, called AtomicReference. The operable object of the AtomicReference class is generic, so custom classes are supported. There is no self-increment method at the bottom. The operating method can be passed as a function input parameter. The source code is as follows:

/ / perform accumulatorFunction operation on x / / accumulatorFunction is a function, you can customize what you want to do / / return the old value public final V getAndAccumulate (V x, BinaryOperator accumulatorFunction) {/ / prev is the old value, next is the new value V prev, next; / / spin + CAS guarantee that you can replace the old value do {prev = get () / / execute custom action next = accumulatorFunction.apply (prev, x);} while (! compareAndSet (prev, next)); return prev;} so far, I believe you have a deeper understanding of "CountDownLatch and Atomic atomic operation class source code analysis". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!

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: 266

*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