JUC(java.util.Concurrent) began, arguably, with the Unsafe class.

Introduction of Unsafe

Unsafe, as its name implies, is an Unsafe class under Sun. misc. The Unsafe class does not operate under the Java standard, and most of Java’s memory operations are handed over to the JVM. The Unsafe class has the ability to manipulate memory directly, like Pointers in C. It also brings up the problem of Pointers. Overusing the Unsafe class, which is why it is officially named Unsafe, makes errors even more likely, and is not recommended, even without annotations.

To be safe with Unsafe, the Unsafe class is only allowed for classes that come with the JDK, as you can see in the following code

 public static Unsafe getUnsafe(a) { Class<? > caller = Reflection.getCallerClass();if(! VM.isSystemDomainLoader(caller.getClassLoader()))throw new SecurityException("Unsafe");
     return theUnsafe;
 }
Copy the code

If the current Class is not system-loaded (that is, caller.getClassLoader() is not empty), throw a SecurityException.

After Java9, there was a JDK.internal.misc.Unsafe class, which does the same thing as sun.misc.Unsafe, except when getSafe(), Unsafe is unchecked, but code in the JDK package cannot be called directly during application development, and since Java9, both Unsafe classes are adequately annotated.

  • To obtain the Unsafe

The Unsafe class has such a field.

private static final Unsafe theUnsafe = new Unsafe();
Copy the code

Even though you can’t retrieve an Unsafe object directly, you can still retrieve it through reflection.

private static Unsafe getUnsafe(a) throws NoSuchFieldException, IllegalAccessException {
     Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
     theUnsafeField.setAccessible(true);
     return (Unsafe) theUnsafeField.get(Unsafe.class);
}
Copy the code

JUC, on the other hand, uses the Unsafe functionality closely.

Function introduction

Unsafe classes are divided into memory operations, CAS, Class related, object operations, array related, memory barriers, system related, and thread scheduling functions.

Memory operations

  • Native memory operations
// Allocate memory and return the memory address
public native long allocateMemory(long bytes);
Address can be the address returned by the allocateMemory method, and bytes is the size of the extension
public native long reallocateMemory(long address, long bytes);
// Free memory
public native void freeMemory(long address);
// Set the default value for the given memory block
public native void setMemory(long address, long bytes, byte value);
// Gets the byte type of the specified address value
public native byte getByte(long address);
// Sets the byte type value of the specified value outside the heap
public native void putByte(long address, byte x);
Copy the code
  • In-heap memory operations
// Set the default value for the given memory block
public native void setMemory(Object o, long offset, long bytes, byte value);
// Memory copy
public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes);
/ / get the value of the specified address offset for o object offset of a field similar to have: get int, getDouble, getLong, getChar, etc
public native Object getObject(Object o, long offset);
/ / setting, offset for o object offset of a field, similar and putInt putDouble, putLong, putChar, etc
public native void putObject(Object o, long offset, Object x);
Copy the code

In general, objects created in Java are in heap memory, which is managed by the JVM and follows the JVM memory management mechanism. In contrast, out-of-heap memory, which is out of the JAVA core, relies on the native methods for handling out-of-heap memory that Unsafe provides.

Java zone of Java NIO. NIO. DirectByteBuffer is to use Unsafe in the heap memory function to operate outside the heap memory. Method of use

ByteBuffer.allocateDirect(1024);
Copy the code

UNSAFE is eventually called in the constructor of DirectByteBuffer to allocate and initialize memory.

long base = 0;
try {
    base = UNSAFE.allocateMemory(size);
} catch (OutOfMemoryError x) {
    Bits.unreserveMemory(size, cap);
    throw x;
}
UNSAFE.setMemory(base, size, (byte) 0);
Copy the code
  • Use case

Out of memory

Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(Unsafe.class);
long address = unsafe.allocateMemory(1024);
unsafe.setMemory(address, 1024, (byte) 0);
unsafe.putInt(address, 1);
System.out.println(unsafe.getInt(address));
unsafe.freeMemory(address);
Copy the code

Within the heap memory

public class Demo {
    private static String name = "jfound";
    private int age = 10;
}
//-----------
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(Unsafe.class);

Demo demo = new Demo();
Field ageField = Demo.class.getDeclaredField("age");
ageField.setAccessible(true);
long ageFieldAddress = unsafe.objectFieldOffset(ageField);
System.out.println(unsafe.getInt(demo, ageFieldAddress));
Field nameField = Demo.class.getDeclaredField("name");
nameField.setAccessible(true);

unsafe.ensureClassInitialized(Demo.class); // Initialize, otherwise name is null
long nameAddress = unsafe.staticFieldOffset(nameField);
System.out.println(unsafe.getObject(Demo.class, nameAddress)); / / output jfound
Copy the code

CAS

The CAS operation contains three operands — the memory location, the expected old value, and the new value. 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.

This operation is CPU instruction CMPXCHG, which belongs to the instruction level and has atomic properties. Typically, the AQS class locks are implemented using the CAS API under Unsafe.

  • api
//cas changes the value, o is the changed object, which can be class, offset is the specified field offset, expected is the expected value, expected is the changed value
public final native boolean compareAndSwapObject(Object o, long offset,  Object expected, Object update);
public final native boolean compareAndSwapInt(Object o, long offset, int expected,int update);
public final native boolean compareAndSwapLong(Object o, long offset, long expected, long update);
Copy the code
  • example
public class Demo {
    private static String name = "jfound";
}
//--------
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(Unsafe.class);

Field nameField = Demo.class.getDeclaredField("name");
nameField.setAccessible(true);
long nameAddress = unsafe.staticFieldOffset(nameField);
unsafe.ensureClassInitialized(Demo.class); // Initialize, otherwise name is null
unsafe.compareAndSwapObject(Demo.class, nameAddress, "jfound"."jfound-plus");
System.out.println(unsafe.getObject(Demo.class, nameAddress)); / / output jfound - plus
Copy the code

The Class related

  • api
// Get the memory address offset of the given static field. This value is unique and fixed for the given field
public native long staticFieldOffset(Field f);
// Get an object pointer to a given field in a static class
public native Object staticFieldBase(Field f);
// Determine whether a class needs to be initialized, usually when fetching a class's static attributes (because a class's static attributes are not initialized if it is not initialized). Return false if and only if the ensureClassInitialized method is not in effect.
public native boolean shouldBeInitialized(Class
        c);
// Check whether the given class is already initialized. Usually used when retrieving a class's static properties (because a class's static properties are not initialized if it is not initialized).
public native void ensureClassInitialized(Class
        c);
// Define a class that skips all security checks for the JVM. By default, instances of ClassLoader and ProtectionDomain come from the caller
public nativeClass<? > defineClass(String name,byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain);
// Define an anonymous class
public nativeClass<? > defineAnonymousClass(Class<? > hostClass,byte[] data, Object[] cpPatches);
Copy the code
  • example
public static class Demo {
    private static String name = "jfound";
}
// -----------   
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(Unsafe.class);

System.out.println(unsafe.shouldBeInitialized(Demo.class)); //true for initialization
unsafe.ensureClassInitialized(Demo.class); / / initialization
System.out.println(unsafe.shouldBeInitialized(Demo.class)); // Fale, already initialized

Field nameField = Demo.class.getDeclaredField("name");
nameField.setAccessible(true);
long nameAddress = unsafe.staticFieldOffset(nameField);
System.out.println(unsafe.getObject(unsafe.staticFieldBase(nameField), nameAddress));
Copy the code

The object operation

  • api
// Returns the offset of the memory address of an object member attribute relative to the memory address of the object
public native long objectFieldOffset(Field f);
GetInt, getDouble, getLong, getChar, etc
public native Object getObject(Object o, long offset);
PutInt, putDouble, putLong, putChar, etc
public native void putObject(Object o, long offset, Object x);
// Get a reference to a variable from the specified offset of the object, using the loading semantics of volatile
public native Object getObjectVolatile(Object o, long offset);
// Store references to variables at the specified offset of the object, using the storage semantics of volatile
public native void putObjectVolatile(Object o, long offset, Object x);
// The ordered, delayed version of putObjectVolatile does not guarantee that a value change is immediately visible to other threads. Only valid if the field is modified by the volatile modifier
public native void putOrderedObject(Object o, long offset, Object x);
// Bypass the constructor and initialize the code to create the object
public native Object allocateInstance(Class
        cls) throws InstantiationException;
Copy the code
  • demo
public static class Demo {
    private String name = "jfound";
    public Demo(a) {
        System.out.println("Constructor"); }}//--------
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(Unsafe.class);

Field nameField = Demo.class.getDeclaredField("name");

Demo demo = (Demo) unsafe.allocateInstance(Demo.class); // The constructor is not called
long offset = unsafe.objectFieldOffset(nameField);
System.out.println(unsafe.getObject(demo, offset)); // Print null, allocateInstance out of the object, the property is not initialized
System.out.println(unsafe.getObject(new Demo(), offset)); //jfound
Copy the code

Interestingly, the object created by allocateInstance does not call the constructor, and its properties are not initialized.

An array of relevant

  • api
// Returns the offset address of the first element in the array
public native int arrayBaseOffset(Class
        arrayClass);
// Return the size of an element in the array
public native int arrayIndexScale(Class
        arrayClass);
Copy the code
  • demo
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(Unsafe.class);

System.out.println(unsafe.arrayBaseOffset(int[].class));
System.out.println(unsafe.arrayIndexScale(int[].class));
Copy the code

The memory barrier

  • api
// Memory barrier prevents reordering of loads. The load operations in front of the barrier cannot be reordered to the back of the barrier
public native void loadFence(a);
// Memory barrier prevents store operations from reordering. Store operations in front of the barrier cannot be reordered to the back of the barrier. Store operations behind the barrier cannot be reordered to the front of the barrier
public native void storeFence(a);
// Memory barrier prevents reordering of load and store operations
public native void fullFence(a);
Copy the code

System related

// Returns the system pointer size. The return value is 4 (32-bit systems) or 8 (64-bit systems).
public native int addressSize(a);  
// The size of the memory page, which is a power of 2.
public native int pageSize(a);
Copy the code

Thread scheduling

This section includes methods for thread suspension, recovery, locking, etc.

  • api
// Block the thread
public native void park(boolean isAbsolute, long time);
// Unblock the thread
public native void unpark(Object thread);
Copy the code

park

Block the current thread until an unpark method appears (called), a method for unpark has appeared (called before this park method was called), the thread is interrupted, or time expires (that is, the blocking timeout). In the case of non-zero time, if isAbsolute is true, time is a millisecond relative to the epoch, otherwise time means nanosecond

unpark

Releases a block on a thread created by Park. This method can also be used to terminate a block caused by a previous call to park. Thread has not been destroyed because this operation is not secure.

Search JFound on wechat to retrieve unsafe mind map to retrieve juc’s mind map