This is the first day of my participation in the Gwen Challenge in November. Check out the details: the last Gwen Challenge in 2021

preface

Looking at the source code in JUC and distributing packages, the Unsafe class is at the heart of the underlying implementation of Java and distributing packages. The Unsafe class gives Java the ability to manipulate memory Spaces like Pointers to THE C language. The Unsafe class provides atomic operations at the hardware level. The Unsafe methods are native methods that use JNI to access the native C++ implementation library. Take a closer look at the Unsafe class.

The main method

Unsafe has many ways to modify public Native, as well as dozens of others based on the Public Native method. But it can be broadly divided into the following categories:

(1) Initialization operation

(2) Operate object properties

(3) Operate on array elements

(4) Thread hangs and resumes

(5) CAS mechanism

Looking beyond the broadening source, here’s a closer look at the broadening source.

Manipulating property methods

// Get the reference value from the given Java variable. This is actually getting the value of a property in a Java object O that has an offset address. This method can override the suppression of modifiers by ignoring the private, protected, and default modifiers. Similar methods are getInt, getDouble, and so on. The same thing with putObject. public native Object getObject(Object o, long offset);Copy the code
// Force an attribute value from main memory. Similar methods are getIntVolatile, getDoubleVolatile, and so on. The same goes for putObjectVolatile. public native Object getObjectVolatile(Object o, long offset);Copy the code
// Set the Object field corresponding to the offset address in the o Object to the specified value x. This is an ordered or delayed putObjectVolatile method, and there is no guarantee that the value change will be immediately visible to other threads. This is valid only if the field is volatile and expected to be modified. Similar methods are putOrderedInt and putOrderedLong. public native void putOrderedObject(Object o, long offset, Object x);Copy the code
// Returns the location (offset address) of the given static property in its class's storage allocation. public native long staticFieldOffset(Field f); // Returns the location (offset address) of the given non-static property in its class's storage allocation. public native long objectFieldOffset(Field f); // Returns the location of the given static property, used with the staticFieldOffset method. public native Object staticFieldBase(Field f);Copy the code

Operating an array

Public native int arrayBaseOffset(Class arrayClass); // Return the offset address of the first element of the array type. // Returns the increment of the offset address between elements in the array. These two methods work together to locate the address of any element. public native int arrayIndexScale(Class arrayClass);Copy the code

Memory management

// Gets the size of the local pointer in bytes, usually 4 or 8. The constant ADDRESS_SIZE is called to this method. public native int addressSize(); // Gets the number of pages in local memory, a power of 2. public native int pageSize(); Public native long allocateMemory(long bytes) public native allocateMemory(long bytes) specifies the size of the memory block. // Resize the local memory block using the specified memory address address. The adjusted memory block size is specified in bytes. public native long reallocateMemory(long address, long bytes); // Sets all bytes in a given memory block to a fixed value (usually 0). public native void setMemory(Object o, long offset, long bytes, byte value);Copy the code

The memory barrier

// All reads before this method must be executed before the load barrier. public native void loadFence(); Public native void storeFence(); public native void storeFence(); // All read and write operations before this method must be performed before the full barrier, which is a combination of the load barrier and store barrier. public native void fullFence();Copy the code

Threads hang and resume

// Release a block on a thread created by Park. Because of its insecurity, it is necessary to ensure that threads are alive public native void unpark(Object thread); // Block the current thread until the unpark method is called public native void park(Boolean isAbsolute, long time); `Copy the code

Mechanism of the CAS

The Unsafe class has a number of compare and set and compare and exchange methods. These methods all point to a native method as the underlying implementation.

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);

public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
Copy the code

When the CAS operation is performed, the value of the memory location is compared to the expected value. If it matches, the processor automatically updates the value of the memory location to the new value; otherwise, the processor does nothing

The use of Unsafe

Getting an Unsafe instance

Unsafe provides the getUnsafe method, as follows:

public class Main { public static void main(String[] args) { Unsafe.getUnsafe(); }}Copy the code

Using it directly will raise an exception:

Exception in thread "main" java.lang.SecurityException: Unsafe
at sun.misc.Unsafe.getUnsafe(Unsafe.java:90)
at com.bendcap.java.jvm.unsafe.Main.main(Main.java:13)
Copy the code

Because the Unsafe class is primarily for internal USE in the JDK, it is not meant to be invoked by ordinary users. But it is still possible to get an instance through reflection:

public static Unsafe testUnsafe() { try { Class<? > unsafeClass = Class.forName("sun.misc.Unsafe"); Field field = unsafeClass.getDeclaredField("theUnsafe"); field.setAccessible(true); Unsafe unsafe = (Unsafe) field.get(null); return unsafe; } catch (Exception e) { e.printStackTrace(); } return null; }Copy the code

Changing private fields

Suppose there are the following classes:

public class UnsafeDemo { private int juejin = 0; public boolean juejinDisclosed() { return juejin == 1; }}Copy the code

Using Unsafe, we’ll change the value of the private property Juejin.

UnsafeDemo ud = new UnsafeDemo();
Field field = null;
try {
    field = ud.getClass().getDeclaredField("juejin");
} catch (NoSuchFieldException e) {
    e.printStackTrace();
}
Unsafe unsafe = testUnsafe();
unsafe.putInt(ud, unsafe.objectFieldOffset(field), 1);
return ud.juejinDisclosed();
Copy the code

Unbroadening. PutInt directly changes the value of ud’s private property. Once a class’s private property field is obtained through reflection, its value can be manipulated directly.

conclusion

Although Unsafe doesn’t seem to be used, it’s important to learn how to better understand the concurrency principle of JUC.