主要提供一些用于执行低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等。Java 无法直接操作内存,Unsafe类相当于Java的后门,可以通过C++来操作内存。
JDK并不建议直接使用Unsafe类,“Unsafe” 类名的由来:在程序中过度、不正确使用Unsafe类会使得程序出错的概率变大,使得Java这种安全的语言变得不再“安全”。而且JDK底层有很多针对不同平台运行的代码,使用Unsafe类可能会造成一些跨平台的问题。
Unsafe类提供了哪些功能?
DirectByteBuffer:在该类中使用Unsafe对内存进行管理,是Java用于实现堆外内存(直接内存)的一个重要类,通常用在通信过程中做缓冲池,如在Netty、MINA等NIO框架中应用广泛。DirectByteBuffer对于堆外内存的创建、使用、销毁等逻辑均由Unsafe提供的堆外内存API来实现。
LockSupport:线程调度。
AtomicIntegerArray:数组操作,通过计算内存地址的偏移量来确定每个元素所在的位置。
StampedLock:内存屏障。
java.nio.Bits:系统管理, 计算待申请内存所需内存页数量的静态方法,其依赖于Unsafe中pageSize方法获取系统内存页大小实现后续计算逻辑,该类在私包中,无法正常初始化。
在 java.util.concurrent.atomic
包下绝大多数类的源码中都使用到了Unsafe这个类。例如AtomicInteger
:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class AtomicInteger extends Number implements java .io.Serializable { private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value" )); } catch (Exception ex) { throw new Error (ex); } } ... }
在Unsafe类中有以下三个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 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) ;
使用Unsafe类的方法对线程不安全的add()方法进行修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 private int value = 0 ;private static Unsafe unsafe;private static long valueOffset;static { try { Field field = Unsafe.class.getDeclaredField("theUnsafe" ); field.setAccessible(true ); unsafe = (Unsafe) field.get(null ); valueOffset = unsafe.objectFieldOffset(CASDemo.class.getDeclaredField("value" )); } catch (Exception e) { e.printStackTrace(); } } public void add () { int current, target; do { current = unsafe.getIntVolatile(this , valueOffset); target = current + 1 ; } while (!unsafe.compareAndSwapInt(this , valueOffset, current, target)); } public void test2 () throws InterruptedException { for (int i = 0 ; i < 2 ; i++) { new Thread (() -> { for (int j = 0 ; j < 1000 ; j++) { add(); } }).start(); } TimeUnit.SECONDS.sleep(2 ); System.out.println(value); }