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

The Source Code Analysis of Unsafe Class and the usage of Unsafe Class in Java concurrent programming

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

Share

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

This article introduces the "Java concurrent programming Unsafe class source code analysis and the use of Unsafe class" related knowledge, in the actual case of the operation process, many people will encounter such a dilemma, and then let the editor lead you to learn how to deal with these situations! I hope you can read it carefully and be able to achieve something!

I. Source code analysis of Unsafe class

The Unsafe class in JDK's rt.jar package provides hardware-level atomic operations. The methods in Unsafe are all native methods that access the local C++ implementation library by using JNI.

The main functions of Unsafe class in rt.jar are explained. Unsafe class provides hardware-level atomic operation, which can safely operate memory variables directly. It is widely used in JUC source code. Understanding its principle lays a foundation for studying JUC source code.

First, let's take a look at the use of the main methods in the Unsafe class, as follows:

1.long objectFieldOffset (Field field) method: returns the specified variable at the memory offset address of the class to which it belongs, which is used only when accessing the specified field in the Unsafe function. The following code uses unsafe to get the memory offset of the variable value in AtomicLong in the AtomicLong object, as follows:

Static {try {valueOffset = unsafe.objectFieldOffset (AtomicLong.class.getDeclaredField ("value"));} catch (Exception ex) {throw new Error (ex);}}

The 2.int arrayBaseOffset (Class arrayClass) method: gets the address of the first element in the array

3.int arrayIndexScale (Class arrayClass) method: gets the number of bytes occupied by a single element in the array

3.boolean compareAndSwapLong (Object obj,long offset,long expect,long update) method: compares whether the value of the variable offset offset in the object obj is equal to expect, updates it with the update value, and then returns true, otherwise returns false.

4.public native long getLongVolative (Object obj,long offset) method: gets the value of the volative memory semantics corresponding to the variable that offsets offset in the object obj.

5.void putOrderedLong (Object obj, long offset, long value) method: set the value of the long type field corresponding to the offset offset address in the obj object to value. This is a delayed putLongVolatile method and does not guarantee that value changes are immediately visible to other threads. Variables are useful only if they are decorated with volatile and expect to be accidentally modified.

6.void park (boolean isAbsolute, long time) method: block the current thread, where the parameter isAbsolute equals false, time equals 0 means blocking all the time, and time greater than 0 means that the blocked thread will be awakened after waiting for the specified time. This time is a relative value, an incremental value, that is, the current thread will be awakened after the time is accumulated relative to the current time. If isAbsolute is equal to true, and if time is greater than 0, it will be awakened after blocking to the specified point in time, where time is an absolute time, which is the value converted to ms at a certain point in time. In addition, when another thread interrupts the current thread by calling the interrupt method of the current blocking thread, the current thread also returns, and when the other thread calls the unpark method and takes the current thread as a parameter, the current thread returns.

The 7.void unpark (Object thread) method: wakes up the thread blocked after the call to park, with the argument to the thread that needs to be woken up.

Several new methods have been added to JDK1.8. The simple methods for listing Long type operations are as follows:

The 8.long getAndSetLong (Object obj, long offset, long update) method: gets the value of the volatile semantics of the variable whose offset is offset in the object obj, and sets the semantic value of the variable volatile to update. The usage is as follows:

Public final long getAndSetLong (Object obj, long offset, long update) {long l; do {l = getLongVolatile (obj, offset); / / (1)} while (! compareAndSwapLong (obj, offset, l, update)); return 1;}

From the code can be internal code (1) to use getLongVolative to get the value of the current variable, and then use the CAS atomic operation to set the new value, here the use of while loop is to take into account the situation that multiple threads call at the same time CAS failure requires spin retry.

9.long getAndAddLong (Object obj, long offset, long addValue) method: gets the semantic value of the variable volatile whose offset is offset in the object obj, and sets the value of the variable to the original value + addValue. The usage is as follows:

Public final long getAndAddLong (Object obj, long offset, long addValue) {long l; do {l = getLongVolatile (obj, offset);} while (! compareAndSwapLong (obj, offset, l, l + addValue)); return l;}

Similar to the implementation of getAndSetLong, except that CAS is used here with the original value + the value of the passed incremental parameter addValue.

So how do you use the Unsafe class?

Do you want to practice when you see that the Unsafe class is so awesome? well, first of all, take a look at the following code:

Package com.hjc;import sun.misc.Unsafe;/** * Created by cong on 2018-6-6. * / public class TestUnSafe {/ / get the instance of Unsafe (2.2.1) static final Unsafe unsafe = Unsafe.getUnsafe (); / / record the offset value of variable state in class TestUnSafe (2.2.2) static final long stateOffset; / / variable (2.2.3) private volatile long state = 0 Static {try {/ / gets the offset value of the state variable in class TestUnSafe (2.2.4) stateOffset = unsafe.objectFieldOffset (TestUnSafe.class.getDeclaredField ("state"));} catch (Exception ex) {System.out.println (ex.getLocalizedMessage ()); throw new Error (ex);}} public static void main (String [] args) {/ / create an instance and set the state value to 1 (2.2.5) TestUnSafe test = new TestUnSafe () / / (2.2.6) Boolean sucess = unsafe.compareAndSwapInt (test, stateOffset, 0,1); System.out.println (sucess);}

Code (2. 2. 1) gets an instance of Unsafe, and code (2. 2. 3) creates a variable state initialized to 0. 3.

The code (2.2.4) uses unsafe.objectFieldOffset to get the memory offset address of the state variable in the TestUnSafe class in the TestUnSafe object and save it to the stateOffset variable.

The code (2.2.6) calls the compareAndSwapInt method of the created unsafe instance and sets the value of the state variable of the test object. Specifically, if the variable of the test object memory offset is stateOffset's state is 0, the update value is changed to 1.

In the above code, we want to enter true, but the following result will be output after execution:

What causes it? It is necessary to enter the getUnsafe code such as to see what is done in it:

Private static final Unsafe theUnsafe = new Unsafe (); public static Unsafe getUnsafe () {/ / (2.2.7) Class localClass = Reflection.getCallerClass (); / / (2.2.8) if (! VM.isSystemDomainLoader (localClass.getClassLoader () {throw new SecurityException ("Unsafe");} return theUnsafe;} / / determine whether paramClassLoader is a BootStrap class loader (2.2.9) public static boolean isSystemDomainLoader (ClassLoader paramClassLoader) {return paramClassLoader = = null;}

The code (2.2.7) gets the Class object of the object that calls the getUnsafe method, in this case TestUnSafe.calss.

The code (2.2.8) determines whether the localClass is loaded by the Bootstrap class loader, and the key here depends on whether the Bootstrap loader loads TestUnSafe.class. Anyone who has seen the class loading mechanism of the Java virtual machine can clearly see that it is because TestUnSafe.class is loaded using AppClassLoader, so the exception is thrown directly.

So the question is, why do you need such a judgment?

We know that the Unsafe class is provided in rt.jar, while the class in rt.jar is loaded using the Bootstrap class loader, and the class in which we started the main function is loaded using AppClassLoader, so when loading the Unsafe class in the main function, the parent delegation mechanism will delegate to Bootstrap to load the Unsafe class.

If there is no code (2.2.8) this authentication, then our application can use Unsafe to do things, and Unsafe class can directly operate memory, is very insecure, so the JDK development team deliberately made this restriction, do not allow developers to use Unsafe classes in the formal channels, but in the core classes in rt.jar to use Unsafe functionality.

The question is, what if we really want to instantiate the Unsafe class and use the functionality of Unsafe?

Let's not forget reflection this cool techs, use omnipotent reflection to get the instance method of Unsafe, the code is as follows:

Package com.hjc;import sun.misc.Unsafe;import java.lang.reflect.Field;/** * Created by cong on 2018-6-6. * / public class TestUnSafe {static final Unsafe unsafe; static final long stateOffset; private volatile long state = 0; static {try {/ / reflection gets the member variable theUnsafe (2.2.10) Field field = Unsafe.class.getDeclaredField ("theUnsafe") of Unsafe; / / set to accessible (2.2.11) field.setAccessible (true) / / get the value of this variable (2.2.12) unsafe = (Unsafe) field.get (null); / / get the offset of state in TestUnSafe (2.2.13) stateOffset = unsafe.objectFieldOffset ("state");} catch (Exception ex) {System.out.println (ex.getLocalizedMessage ()); throw new Error (ex);}} public static void main (String [] args) {TestUnSafe test = new TestUnSafe () Boolean sucess = unsafe.compareAndSwapInt (test, stateOffset, 0,1); System.out.println (sucess);}}

If the above code (2.2.10 2.2.11 2.2.12) reflects to get an instance of unsafe, the running result is as follows:

II. Exploration of LockSupport class source code

LockSupport in rt.jar in JDK is a utility class that suspends and wakes up threads and is the basis for creating locks and other synchronization classes.

The LockSupport class is associated with a license for each thread that uses it. The thread that calls the method of the LockSupport class by default does not hold a license, and the LockSupport is implemented internally using the Unsafe class.

Note several important functions of LockSupport here, as follows:

1.void park () method: if the thread calling park () has obtained the license associated with LockSupport, calling LockSupport.park () will return immediately, otherwise the calling thread will be prohibited from participating in thread scheduling, that is, it will be blocked and suspended. An example is the following code:

Package com.hjc;import java.util.concurrent.locks.LockSupport;/** * Created by cong on 2018-6-6. * / public class LockSupportTest {public static void main (String [] args) {System.out.println ("park start!"); LockSupport.park (); System.out.println ("park stop!");}}

As shown in the above code, calling the park method directly in the main function will only output park start! The current thread is then suspended because the default calling thread does not hold a license. The running results are as follows:

When you see another thread calling the unpark (Thread thread) method and the current thread as a parameter, the thread that calls the park method will return, in addition, other threads will call the blocking thread's interrupt () method, and the blocked thread will return when the interrupt flag is set or because of the false awakening of the thread, so it is best to call park () by loop condition judgment.

It is important to note that a blocked thread that calls the park () method is interrupted by another thread and the blocked thread returns without throwing an InterruptedException exception.

The 2.void unpark (Thread thread) method when a thread calls unpark, if the parameter thread thread does not hold the license that thread is associated with the LockSupport class, let the thread thread hold it. If park () was suspended after a previous call to thread, the thread will be woken up after calling unpark.

If thread has not called park before, the unPark method will be returned immediately after the park () method is called. The above code is modified as follows:

Package com.hjc;import java.util.concurrent.locks.LockSupport;/** * Created by cong on 2018-6-6. * / public class LockSupportTest {public static void main (String [] args) {System.out.println ("park start!"); / / enable the current thread to obtain the license LockSupport.unpark (Thread.currentThread ()); / / call park LockSupport.park () again; System.out.println ("park stop!");}}

The running results are as follows:

Next, let's look at an example to deepen our understanding of park,unpark. The code is as follows:

Import java.util.concurrent.locks.LockSupport;/** * Created by cong on 2018-6-6. * / public class LockSupportTest {public static void main (String [] args) throws InterruptedException {Thread thread = new Thread (new Runnable () {@ Override public void run () {System.out.println ("child thread park start!"); / / call the park method to suspend your LockSupport.park (); System.out.println ("child thread unpark!");}}) / / start child thread thread.start (); / / main thread hibernate 1s Thread.sleep (1000); System.out.println ("main thread unpark start!"); / / call unpark to make the thread thread hold the license, and then the park method returns LockSupport.unpark (thread);}}

The running results are as follows:

The above code first creates a child thread thread. After startup, the child thread calls the park method. Since the default child thread does not hold a license, it will suspend itself.

The main thread hibernates for 1s so that the main thread calls the unpark method to let the child thread output the child thread park start! And block.

The main thread then executes the unpark method, with the argument to the child thread, so that the child thread holds the license, and the park method called by the child thread returns.

When the park method returns, it will not tell you the reason for the return, so the caller needs to check again whether the condition is met according to what the current park method was called, and if not, call the park method again.

For example, the interrupt state of a thread when it returns can be judged by the comparison of the interrupt state before and after the call.

To show that the thread that calls the park method will return after being interrupted, modify the above example code to delete LockSupport.unpark (thread), and then add thread.interrupt (); the code is as follows:

Import java.util.concurrent.locks.LockSupport;/** * Created by cong on 2018-6-6. * / public class LockSupportTest {public static void main (String [] args) throws InterruptedException {Thread thread = new Thread (new Runnable () {@ Override public void run () {System.out.println ("child thread park start!") Call the park method, suspend yourself, and exit the loop while (! Thread.currentThread (). IsInterrupted ()) {LockSupport.park ();} System.out.println ("child thread unpark!");}}); / / start child thread thread.start (); / / main thread hibernate 1s Thread.sleep (1000); System.out.println ("main thread unpark start!"); / / interrupt child thread thread.interrupt () }}

The running results are as follows:

As in the above code, the child thread will not end until the child thread is interrupted, and even if you call the unPark (Thread) child thread, it will not end if the child thread is not interrupted.

3.void parkNanos (long nanos) method: similar to park, if the thread calling park has already obtained the license associated with LockSupport, calling LockSupport.park () will return immediately, except that if it does not get the license, the calling thread will be suspended after nanos time.

Park also supports three methods with blocker parameters, and when a thread calls park without a license, the blocker object is logged inside the thread when it is blocked and suspended.

Use the diagnostic tool to observe the reason why the thread is blocked, and the diagnostic tool gets the blocker object by calling the getBlocker (Thread) method, so JDK recommends that we use the park method with the blocker parameter, and the blocker is set to this, so that when the memory dump troubleshoots the problem, we can know which class is blocked.

Examples are as follows:

Import java.util.concurrent.locks.LockSupport;/** * Created by cong on 2018-6-6. * / public class TestPark {public void testPark () {LockSupport.park (); / / (1)} public static void main (String [] args) {TestPark testPark = new TestPark (); testPark.testPark ();}}

The running results are as follows:

You can see that it is running in blocking, so we should use the tools in the JDK/bin directory to take a look. If you don't know, you are advised to take a look at JVM's monitoring tool first.

When you use jstack pid to view the thread stack after running, you can see the following results:

Then we modify the above code (1) as follows:

LockSupport.park (this); / / (1)

Run it again and use jstack pid to view the results as follows:

As you can see, with the park method with blocker, the thread stack can provide more information about blocking objects.

Then let's take a look at the source code of the park (Object blocker) function, which is as follows:

Public static void park (Object blocker) {/ / get the calling thread Thread t = Thread.currentThread (); / / set the thread's blocker variable setBlocker (t, blocker); / / suspend the thread UNSAFE.park (false, 0L); / / clear the blocker variable after the thread is activated, because the cause setBlocker (t, null) is usually analyzed only when the thread is blocked;}

There is a variable volatile Object parkBlocker in the Thread class that stores the blocker object passed by park, that is, the blocker variable is stored in the member variable of the thread calling the park method.

The 4.void parkNanos (Object blocker, long nanos) function has an extra timeout than park (Object blocker).

The 5.void parkUntil (Object blocker, long deadline) parkUntil source code is as follows:

Public static void parkUntil (Object blocker, long deadline) {Thread t = Thread.currentThread (); setBlocker (t, blocker); / / isAbsolute=true,time=deadline; indicates that UNSAFE.park (true, deadline) is returned after deadline time; setBlocker (t, null);}

You can see that deadline is set in milliseconds, which is converted to millisecond value from 1970 to a certain point in time now. This is different from parkNanos (Object blocker,long nanos) in that the nanos time is calculated from the current time, while the former specifies a point in time.

For example, if we need to wait until 20:34 on June 06, 2018, convert this point in time to the total number of milliseconds from 1970 to this point in time.

Let's look at another example. The code is as follows:

Import java.util.Queue;import java.util.concurrent.ConcurrentLinkedQueue;import java.util.concurrent.atomic.AtomicBoolean;import java.util.concurrent.locks.LockSupport;/** * Created by cong on 2018-6-6. * / public class FIFOMutex {private final AtomicBoolean locked = new AtomicBoolean (false); private final Queue waiters = new ConcurrentLinkedQueue (); public void lock () {boolean wasInterrupted = false; Thread current = Thread.currentThread (); waiters.add (current) / / only the thread at the head of the queue can acquire the lock (1) while (waiters.peek ()! = current | |! locked.compareAndSet (false, true)) {LockSupport.park (this); if (Thread.interrupted ()) / / (2) wasInterrupted = true;} waiters.remove (); if (wasInterrupted) / / (3) current.interrupt ();} public void unlock () {locked.set (false); LockSupport.unpark (waiters.peek ()) }}

You can see that this is a first-in, first-out lock, that is, only the first element of the queue can get the place. Code (1) if the current thread is not the leader of the queue or the current lock has been acquired by another thread, call the park method to suspend itself.

Then the code (2) makes a judgment, if the park method returns because of the interrupt, ignore the interrupt, and reset the interrupt flag, only make a mark, and then again determine whether the current thread is the first element of the line or if the lock has been acquired by other threads, if so, continue to call the park method to suspend itself.

Then the thread is interrupted if it is marked true in code (3). How do you understand this? In fact, other threads interrupted the thread, although I am not interested in the interrupt signal, ignore it, but it does not mean that other threads are not interested in the flag, so to restore.

"Java concurrent programming Unsafe class source code analysis and how to use the Unsafe class" content is introduced here, thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

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