Atomic 类的原子操作是依赖java中的魔法类sun.misc.Unsafe来实现的,而这个类为我们提供了访问底层的机制,这种机制仅供java核心类库使用,而不应该被普通用户使用。
获取Unsafe的实例
查看Unsafe的源码我们会发现它提供了一个getUnsafe()的静态方法。
@CallerSensitive public static Unsafe getUnsafe() { Class var0 = Reflection.getCallerClass(); if (!VM.isSystemDomainLoader(var0.getClassLoader())) { throw new SecurityException("Unsafe"); } else { return theUnsafe; } }
如果直接调用这个方法会抛出一个SecurityException异常,这是因为Unsafe仅供java内部类使用,外部类不应该使用它。通过反射拿到它的实例
public class UnsafeTest { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); Unsafe unsafe = (Unsafe) f.get(null); } }
使用Unsafe实例化一个类
假如我们有一个简单的类如下:
class User { int age; public User() { this.age = 10; } }
如果我们通过构造方法实例化这个类,age属性将会返回10。
User user1 = new User(); // 打印10 System.out.println(user1.age);
如果我们调用Unsafe来实例化呢?
User user2 = (User) unsafe.allocateInstance(User.class); // 打印0 System.out.println(user2.age);
age将返回0,因为 Unsafe.allocateInstance()
只会给对象分配内存,并不会调用构造方法,所以这里只会返回int类型的默认值0。
修改私有字段的值
使用Unsafe的putXXX()方法,我们可以修改任意私有字段的值。
public class UnsafeTest { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); Unsafe unsafe = (Unsafe) f.get(null); User user = new User(); Field age = user.getClass().getDeclaredField("age"); unsafe.putInt(user, unsafe.objectFieldOffset(age), 20); // 打印20 System.out.println(user.getAge()); } } class User { private int age; public User() { this.age = 10; } public int getAge() { return age; } }
一旦我们通过反射调用得到字段age,我们就可以使用Unsafe将其值更改为任何其他int值。(当然,这里也可以通过反射直接修改)
抛出checked异常
我们知道如果代码抛出了checked异常,要不就使用try...catch捕获它,要不就在方法签名上定义这个异常,但是,通过Unsafe我们可以抛出一个checked异常,同时却不用捕获或在方法签名上定义它。
// 使用正常方式抛出IOException需要定义在方法签名上往外抛 public static void readFile() throws IOException { throw new IOException(); } // 使用Unsafe抛出异常不需要定义在方法签名上往外抛 public static void readFileUnsafe() { unsafe.throwException(new IOException()); }
使用堆外内存
如果进程在运行过程中JVM上的内存不足了,会导致频繁的进行GC。理想情况下,我们可以考虑使用堆外内存,这是一块不受JVM管理的内存。
使用Unsafe的allocateMemory()我们可以直接在堆外分配内存,这可能非常有用,但我们要记住,这个内存不受JVM管理,因此我们要调用freeMemory()方法手动释放它。
假设我们要在堆外创建一个巨大的int数组,我们可以使用allocateMemory()方法来实现:
class OffHeapArray { // 一个int等于4个字节 private static final int INT = 4; private long size; private long address; private static Unsafe unsafe; static { try { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); unsafe = (Unsafe) f.get(null); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } // 构造方法,分配内存 public OffHeapArray(long size) { this.size = size; // 参数字节数 address = unsafe.allocateMemory(size * INT); } // 获取指定索引处的元素 public int get(long i) { return unsafe.getInt(address + i * INT); } // 设置指定索引处的元素 public void set(long i, int value) { unsafe.putInt(address + i * INT, value); } // 元素个数 public long size() { return size; } // 释放堆外内存 public void freeMemory() { unsafe.freeMemory(address); } }
在构造方法中调用allocateMemory()分配内存,在使用完成后调用freeMemory()释放内存。
使用方式如下:
OffHeapArray offHeapArray = new OffHeapArray(4); offHeapArray.set(0, 1); offHeapArray.set(1, 2); offHeapArray.set(2, 3); offHeapArray.set(3, 4); offHeapArray.set(2, 5); // 在索引2的位置重复放入元素 int sum = 0; for (int i = 0; i < offHeapArray.size(); i++) { sum += offHeapArray.get(i); } // 打印12 System.out.println(sum); offHeapArray.freeMemory();
最后,一定要记得调用freeMemory()将内存释放回操作系统。
CompareAndSwap操作
JUC下面大量使用了CAS操作,它们的底层是调用的Unsafe的CompareAndSwapXXX()方法。这种方式广泛运用于无锁算法,与java中标准的悲观锁机制相比,它可以利用CAS处理器指令提供极大的加速。
比如,我们可以基于Unsafe的compareAndSwapInt()方法构建线程安全的计数器。
class Counter { private volatile int count = 0; private static long offset; private static Unsafe unsafe; static { try { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); unsafe = (Unsafe) f.get(null); offset = unsafe.objectFieldOffset(Counter.class.getDeclaredField("count")); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } public void increment() { int before = count; // 失败了就重试直到成功为止 while (!unsafe.compareAndSwapInt(this, offset, before, before + 1)) { before = count; } } public int getCount() { return count; } }
我们定义了一个volatile的字段count,以便对它的修改所有线程都可见,并在类加载的时候获取count在类中的偏移地址。
在increment()方法中,我们通过调用Unsafe的compareAndSwapInt()方法来尝试更新之前获取到的count的值,如果它没有被其它线程更新过,则更新成功,否则不断重试直到成功为止。
我们可以通过使用多个线程来测试我们的代码:
Counter counter = new Counter(); ExecutorService threadPool = Executors.newFixedThreadPool(100); // 起100个线程,每个线程自增10000次 IntStream.range(0, 100) .forEach(i->threadPool.submit(()->IntStream.range(0, 10000) .forEach(j->counter.increment()))); threadPool.shutdown(); Thread.sleep(2000); // 打印1000000 System.out.println(counter.getCount());
park/unpark
JVM在上下文切换的时候使用了Unsafe中的两个非常牛逼的方法park()和unpark()。
当一个线程正在等待某个操作时,JVM调用Unsafe的park()方法来阻塞此线程。
当阻塞中的线程需要再次运行时,JVM调用Unsafe的unpark()方法来唤醒此线程。
我们之前在分析java中的集合时看到了大量的LockSupport.park()/unpark(),它们底层都是调用的Unsafe的这两个方法。
总结
使用Unsafe几乎可以操作一切:
(1)实例化一个类;
(2)修改私有字段的值;
(3)抛出checked异常;
(4)使用堆外内存;
(5)CAS操作;
(6)阻塞/唤醒线程;
彩蛋
论实例化一个类的方式?
(1)通过构造方法实例化一个类;
(2)通过Class实例化一个类;
(3)通过反射实例化一个类;
(4)通过克隆实例化一个类;
(5)通过反序列化实例化一个类;
(6)通过Unsafe实例化一个类;
public class InstantialTest { private static Unsafe unsafe; static { try { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); unsafe = (Unsafe) f.get(null); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } public static void main(String[] args) throws Exception { // 1. 构造方法 User user1 = new User(); // 2. Class,里面实际也是反射 User user2 = User.class.newInstance(); // 3. 反射 User user3 = User.class.getConstructor().newInstance(); // 4. 克隆 User user4 = (User) user1.clone(); // 5. 反序列化 User user5 = unserialize(user1); // 6. Unsafe User user6 = (User) unsafe.allocateInstance(User.class); System.out.println(user1.age); System.out.println(user2.age); System.out.println(user3.age); System.out.println(user4.age); System.out.println(user5.age); System.out.println(user6.age); } private static User unserialize(User user1) throws Exception { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D://object.txt")); oos.writeObject(user1); oos.close(); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D://object.txt")); // 反序列化 User user5 = (User) ois.readObject(); ois.close(); return user5; } static class User implements Cloneable, Serializable { private int age; public User() { this.age = 10; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } }
附 Unsafe.class:
package sun.misc; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.security.ProtectionDomain; import sun.reflect.CallerSensitive; import sun.reflect.Reflection; public final class Unsafe { static { registerNatives(); Reflection.registerMethodsToFilter(Unsafe.class, new String[] { "getUnsafe" }); } private static final Unsafe theUnsafe = new Unsafe(); public static final int INVALID_FIELD_OFFSET = -1; @CallerSensitive public static Unsafe getUnsafe() { Class localClass = Reflection.getCallerClass(); if (!VM.isSystemDomainLoader(localClass.getClassLoader())) { throw new SecurityException("Unsafe"); } return theUnsafe; } @Deprecated public int getInt(Object paramObject, int paramInt) { return getInt(paramObject, paramInt); } @Deprecated public void putInt(Object paramObject, int paramInt1, int paramInt2) { putInt(paramObject, paramInt1, paramInt2); } @Deprecated public Object getObject(Object paramObject, int paramInt) { return getObject(paramObject, paramInt); } @Deprecated public void putObject(Object paramObject1, int paramInt, Object paramObject2) { putObject(paramObject1, paramInt, paramObject2); } @Deprecated public boolean getBoolean(Object paramObject, int paramInt) { return getBoolean(paramObject, paramInt); } @Deprecated public void putBoolean(Object paramObject, int paramInt, boolean paramBoolean) { putBoolean(paramObject, paramInt, paramBoolean); } @Deprecated public byte getByte(Object paramObject, int paramInt) { return getByte(paramObject, paramInt); } @Deprecated public void putByte(Object paramObject, int paramInt, byte paramByte) { putByte(paramObject, paramInt, paramByte); } @Deprecated public short getShort(Object paramObject, int paramInt) { return getShort(paramObject, paramInt); } @Deprecated public void putShort(Object paramObject, int paramInt, short paramShort) { putShort(paramObject, paramInt, paramShort); } @Deprecated public char getChar(Object paramObject, int paramInt) { return getChar(paramObject, paramInt); } @Deprecated public void putChar(Object paramObject, int paramInt, char paramChar) { putChar(paramObject, paramInt, paramChar); } @Deprecated public long getLong(Object paramObject, int paramInt) { return getLong(paramObject, paramInt); } @Deprecated public void putLong(Object paramObject, int paramInt, long paramLong) { putLong(paramObject, paramInt, paramLong); } @Deprecated public float getFloat(Object paramObject, int paramInt) { return getFloat(paramObject, paramInt); } @Deprecated public void putFloat(Object paramObject, int paramInt, float paramFloat) { putFloat(paramObject, paramInt, paramFloat); } @Deprecated public double getDouble(Object paramObject, int paramInt) { return getDouble(paramObject, paramInt); } @Deprecated public void putDouble(Object paramObject, int paramInt, double paramDouble) { putDouble(paramObject, paramInt, paramDouble); } public void setMemory(long paramLong1, long paramLong2, byte paramByte) { setMemory(null, paramLong1, paramLong2, paramByte); } public void copyMemory(long paramLong1, long paramLong2, long paramLong3) { copyMemory(null, paramLong1, null, paramLong2, paramLong3); } @Deprecated public int fieldOffset(Field paramField) { if (Modifier.isStatic(paramField.getModifiers())) { return (int)staticFieldOffset(paramField); } return (int)objectFieldOffset(paramField); } @Deprecated public Object staticFieldBase(Class<?> paramClass) { Field[] arrayOfField = paramClass.getDeclaredFields(); for (int i = 0; i < arrayOfField.length; i++) { if (Modifier.isStatic(arrayOfField[i].getModifiers())) { return staticFieldBase(arrayOfField[i]); } } return null; } public static final int ARRAY_BOOLEAN_BASE_OFFSET = theUnsafe .arrayBaseOffset(boolean[].class); public static final int ARRAY_BYTE_BASE_OFFSET = theUnsafe .arrayBaseOffset(byte[].class); public static final int ARRAY_SHORT_BASE_OFFSET = theUnsafe .arrayBaseOffset(short[].class); public static final int ARRAY_CHAR_BASE_OFFSET = theUnsafe .arrayBaseOffset(char[].class); public static final int ARRAY_INT_BASE_OFFSET = theUnsafe .arrayBaseOffset(int[].class); public static final int ARRAY_LONG_BASE_OFFSET = theUnsafe .arrayBaseOffset(long[].class); public static final int ARRAY_FLOAT_BASE_OFFSET = theUnsafe .arrayBaseOffset(float[].class); public static final int ARRAY_DOUBLE_BASE_OFFSET = theUnsafe .arrayBaseOffset(double[].class); public static final int ARRAY_OBJECT_BASE_OFFSET = theUnsafe .arrayBaseOffset(Object[].class); public static final int ARRAY_BOOLEAN_INDEX_SCALE = theUnsafe .arrayIndexScale(boolean[].class); public static final int ARRAY_BYTE_INDEX_SCALE = theUnsafe .arrayIndexScale(byte[].class); public static final int ARRAY_SHORT_INDEX_SCALE = theUnsafe .arrayIndexScale(short[].class); public static final int ARRAY_CHAR_INDEX_SCALE = theUnsafe .arrayIndexScale(char[].class); public static final int ARRAY_INT_INDEX_SCALE = theUnsafe .arrayIndexScale(int[].class); public static final int ARRAY_LONG_INDEX_SCALE = theUnsafe .arrayIndexScale(long[].class); public static final int ARRAY_FLOAT_INDEX_SCALE = theUnsafe .arrayIndexScale(float[].class); public static final int ARRAY_DOUBLE_INDEX_SCALE = theUnsafe .arrayIndexScale(double[].class); public static final int ARRAY_OBJECT_INDEX_SCALE = theUnsafe .arrayIndexScale(Object[].class); public static final int ADDRESS_SIZE = theUnsafe.addressSize(); public final int getAndAddInt(Object paramObject, long paramLong, int paramInt) { int i; do { i = getIntVolatile(paramObject, paramLong); } while (!compareAndSwapInt(paramObject, paramLong, i, i + paramInt)); return i; } public final long getAndAddLong(Object paramObject, long paramLong1, long paramLong2) { long l; do { l = getLongVolatile(paramObject, paramLong1); } while (!compareAndSwapLong(paramObject, paramLong1, l, l + paramLong2)); return l; } public final int getAndSetInt(Object paramObject, long paramLong, int paramInt) { int i; do { i = getIntVolatile(paramObject, paramLong); } while (!compareAndSwapInt(paramObject, paramLong, i, paramInt)); return i; } public final long getAndSetLong(Object paramObject, long paramLong1, long paramLong2) { long l; do { l = getLongVolatile(paramObject, paramLong1); } while (!compareAndSwapLong(paramObject, paramLong1, l, paramLong2)); return l; } public final Object getAndSetObject(Object paramObject1, long paramLong, Object paramObject2) { Object localObject; do { localObject = getObjectVolatile(paramObject1, paramLong); } while (!compareAndSwapObject(paramObject1, paramLong, localObject, paramObject2)); return localObject; } private static void throwIllegalAccessError() { throw new IllegalAccessError(); } private static native void registerNatives(); public native int getInt(Object paramObject, long paramLong); public native void putInt(Object paramObject, long paramLong, int paramInt); public native Object getObject(Object paramObject, long paramLong); public native void putObject(Object paramObject1, long paramLong, Object paramObject2); public native boolean getBoolean(Object paramObject, long paramLong); public native void putBoolean(Object paramObject, long paramLong, boolean paramBoolean); public native byte getByte(Object paramObject, long paramLong); public native void putByte(Object paramObject, long paramLong, byte paramByte); public native short getShort(Object paramObject, long paramLong); public native void putShort(Object paramObject, long paramLong, short paramShort); public native char getChar(Object paramObject, long paramLong); public native void putChar(Object paramObject, long paramLong, char paramChar); public native long getLong(Object paramObject, long paramLong); public native void putLong(Object paramObject, long paramLong1, long paramLong2); public native float getFloat(Object paramObject, long paramLong); public native void putFloat(Object paramObject, long paramLong, float paramFloat); public native double getDouble(Object paramObject, long paramLong); public native void putDouble(Object paramObject, long paramLong, double paramDouble); public native byte getByte(long paramLong); public native void putByte(long paramLong, byte paramByte); public native short getShort(long paramLong); public native void putShort(long paramLong, short paramShort); public native char getChar(long paramLong); public native void putChar(long paramLong, char paramChar); public native int getInt(long paramLong); public native void putInt(long paramLong, int paramInt); public native long getLong(long paramLong); public native void putLong(long paramLong1, long paramLong2); public native float getFloat(long paramLong); public native void putFloat(long paramLong, float paramFloat); public native double getDouble(long paramLong); public native void putDouble(long paramLong, double paramDouble); public native long getAddress(long paramLong); public native void putAddress(long paramLong1, long paramLong2); public native long allocateMemory(long paramLong); public native long reallocateMemory(long paramLong1, long paramLong2); public native void setMemory(Object paramObject, long paramLong1, long paramLong2, byte paramByte); public native void copyMemory(Object paramObject1, long paramLong1, Object paramObject2, long paramLong2, long paramLong3); public native void freeMemory(long paramLong); public native long staticFieldOffset(Field paramField); public native long objectFieldOffset(Field paramField); public native Object staticFieldBase(Field paramField); public native boolean shouldBeInitialized(Class<?> paramClass); public native void ensureClassInitialized(Class<?> paramClass); public native int arrayBaseOffset(Class<?> paramClass); public native int arrayIndexScale(Class<?> paramClass); public native int addressSize(); public native int pageSize(); public native Class<?> defineClass(String paramString, byte[] paramArrayOfByte, int paramInt1, int paramInt2, ClassLoader paramClassLoader, ProtectionDomain paramProtectionDomain); public native Class<?> defineAnonymousClass(Class<?> paramClass, byte[] paramArrayOfByte, Object[] paramArrayOfObject); public native Object allocateInstance(Class<?> paramClass) throws InstantiationException; @Deprecated public native void monitorEnter(Object paramObject); @Deprecated public native void monitorExit(Object paramObject); @Deprecated public native boolean tryMonitorEnter(Object paramObject); public native void throwException(Throwable paramThrowable); public final native boolean compareAndSwapObject(Object paramObject1, long paramLong, Object paramObject2, Object paramObject3); public final native boolean compareAndSwapInt(Object paramObject, long paramLong, int paramInt1, int paramInt2); public final native boolean compareAndSwapLong(Object paramObject, long paramLong1, long paramLong2, long paramLong3); public native Object getObjectVolatile(Object paramObject, long paramLong); public native void putObjectVolatile(Object paramObject1, long paramLong, Object paramObject2); public native int getIntVolatile(Object paramObject, long paramLong); public native void putIntVolatile(Object paramObject, long paramLong, int paramInt); public native boolean getBooleanVolatile(Object paramObject, long paramLong); public native void putBooleanVolatile(Object paramObject, long paramLong, boolean paramBoolean); public native byte getByteVolatile(Object paramObject, long paramLong); public native void putByteVolatile(Object paramObject, long paramLong, byte paramByte); public native short getShortVolatile(Object paramObject, long paramLong); public native void putShortVolatile(Object paramObject, long paramLong, short paramShort); public native char getCharVolatile(Object paramObject, long paramLong); public native void putCharVolatile(Object paramObject, long paramLong, char paramChar); public native long getLongVolatile(Object paramObject, long paramLong); public native void putLongVolatile(Object paramObject, long paramLong1, long paramLong2); public native float getFloatVolatile(Object paramObject, long paramLong); public native void putFloatVolatile(Object paramObject, long paramLong, float paramFloat); public native double getDoubleVolatile(Object paramObject, long paramLong); public native void putDoubleVolatile(Object paramObject, long paramLong, double paramDouble); public native void putOrderedObject(Object paramObject1, long paramLong, Object paramObject2); public native void putOrderedInt(Object paramObject, long paramLong, int paramInt); public native void putOrderedLong(Object paramObject, long paramLong1, long paramLong2); public native void unpark(Object paramObject); public native void park(boolean paramBoolean, long paramLong); public native int getLoadAverage(double[] paramArrayOfDouble, int paramInt); public native void loadFence(); public native void storeFence(); public native void fullFence(); }View Code
原文链接:https://www.cnblogs.com/tong-yuan/p/Unsafe.html