Unsafe类

Java魔法类:Unsafe应用解析
Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等,这些方法在提升Java运行效率、增强Java语言底层资源操作能力方面起到了很大的作用。但由于Unsafe类使Java语言拥有了类似C语言指针一样操作内存空间的能力,这无疑也增加了程序发生相关指针问题的风险。在程序中过度、不正确使用Unsafe类会使得程序出错的概率变大,使得Java这种安全的语言变得不再"安全",因此对Unsafe的使用一定要慎重。 注:本文对sun.misc.Unsafe公共API功能及相关应用场景进行介绍。 如下Unsafe源码所示,Unsafe类为一单例实现,提供静态方法getUnsafe获取Unsafe实例,当且仅当调用getUnsafe方法的类为引导类加载器所加载时才合法,否则抛出SecurityException异常。 public final class Unsafe { // 单例对象 private static final Unsafe theUnsafe; private Unsafe() { } @CallerSensitive public static Unsafe getUnsafe() { Class var0 = Reflection.getCallerClass(); // 仅在引导类加载器`BootstrapClassLoader`加载时才合法 if(!VM.isSystemDomainLoader(var0.getClassLoader())) { throw new SecurityException("Unsafe"); } else { return theUnsafe; } } } 那如若想使用这个类,该如何获取其实例?有如下两个可行方案。 其一,从 getUnsafe方法的使用限制条件出发,通过Java命令行命令-Xbootclasspath/a把调用Unsafe相关方法的类A所在jar包路径追加到默认的bootstrap路径中,使得A被引导类加载器加载,从而通过 Unsafe.getUnsafe 方法安全的获取Unsafe实例。 java
Java魔法类:Unsafe应用解析

Unsafe对象

Unsafe 对象提供了非常底层的,操作内存、线程的方法,Unsafe 对象不能直接调用,只能通过反射获得
public class UnsafeAccessor { static Unsafe unsafe; static { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); unsafe = (Unsafe) theUnsafe.get(null); } catch (NoSuchFieldException | IllegalAccessException e) { throw new Error(e); } } static Unsafe getUnsafe() { return unsafe; } }
public class TestUnsafe { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); Unsafe unsafe = (Unsafe) theUnsafe.get(null); System.out.println(unsafe); // 1. 获取域的偏移地址 long idOffset = unsafe.objectFieldOffset(Teacher.class.getDeclaredField("id")); long nameOffset = unsafe.objectFieldOffset(Teacher.class.getDeclaredField("name")); Teacher t = new Teacher(); // 2. 执行 cas 操作 unsafe.compareAndSwapInt(t, idOffset, 0, 1); unsafe.compareAndSwapObject(t, nameOffset, null, "张三"); // 3. 验证 System.out.println(t); } } @Data class Teacher { volatile int id; volatile String name; }

Park & Unpark

基本使用

它们是 LockSupport 类中的方法
// 暂停当前线程 LockSupport.park(); // 恢复某个线程的运行 LockSupport.unpark(暂停线程对象)
先park 后 unpark
@Slf4j(topic = "c.TestParkUnpark") public class TestParkUnpark { public static void main(String[] args) { Thread t1 = new Thread(() -> { log.debug("start..."); sleep(1); log.debug("park..."); LockSupport.park(); log.debug("resume..."); }, "t1"); t1.start(); sleep(2); log.debug("unpark..."); LockSupport.unpark(t1); } } // 输出 // 13:34:19.631 c.TestParkUnpark [t1] - start... // 13:34:20.642 c.TestParkUnpark [t1] - park... // 13:34:21.635 c.TestParkUnpark [main] - unpark... // 13:34:21.635 c.TestParkUnpark [t1] - resume...
 
先unpark 后park
park失效
@Slf4j(topic = "c.TestParkUnpark") public class TestParkUnpark { public static void main(String[] args) { Thread t1 = new Thread(() -> { log.debug("start..."); sleep(2); log.debug("park..."); LockSupport.park(); log.debug("resume..."); }, "t1"); t1.start(); sleep(1); log.debug("unpark..."); LockSupport.unpark(t1); } } // 输出 //13:35:28.872 c.TestParkUnpark [t1] - start... //13:35:29.875 c.TestParkUnpark [main] - unpark... //13:35:30.883 c.TestParkUnpark [t1] - park... //13:35:30.883 c.TestParkUnpark [t1] - resume...

底层原理

底层是通过c代码实现
先park后unpark的情况
  1. 当前线程调用 Unsafe.park() 方法
  1. 检查 _counter ,本情况为 0,这时,获得 _mutex 互斥锁
  1. 线程进入 _cond 条件变量阻塞
  1. 设置 _counter = 0
    1. 如下图
      notion image
       
 
  1. 调用 Unsafe.unpark(Thread_0) 方法,设置 _counter 为 1
  1. 唤醒 _cond 条件变量中的 Thread_0
  1. Thread_0 恢复运行
  1. 设置 _counter 为 0
notion image
 
先unpark后park的情况
  1. 调用 Unsafe.unpark(Thread_0) 方法,设置 _counter 为 1
  1. _cond 条件变量中无Thread_0,后续不执行
  1. 当前线程调用 Unsafe.park() 方法 检查 _counter ,本情况为 1,这时线程无需阻塞,继续运行
  1. 设置 _counter 为 0
notion image