当前位置 博文首页 > 阳阳的博客:JUC基石——Unsafe类
我们经常在JUC包下的ConcurrentHashMap、Atomic开头的原子操作类、AQS以及LockSupport里面看到Unsafe类的身影,这个Unsafe类究竟是干什么的,本文可以带着读者一探究竟。
Java和C++、C语言的一个重要区别,就是Java中我们无法直接操作一块内存区域,而在C++、C中却可以自己申请内存和释放内存。Unsafe类的设计,为我们提供了手动管理内存的能力。
如同它的名字一样,它被认定为不安全的。直接操纵内存,意味着实例化出来的对象不会受到JVM的管理,不会被GC,需要手动进行回收,容易出现内存泄露的问题。因此,官方并不建议我们在自己的应用程序中使用该类。
public final class Unsafe {
private static final Unsafe theUnsafe;
private Unsafe() {
}
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
//其他方法
}
可以看得出来,该类被final修饰,不允许被继承。构造方法是私有的,在外部不可被实例化。(关于final更多的作用,可以移步这篇文章关键词final的作用)
? ? ? ? 但在内部提供了一个获取单例的getUnsafe()方法,不过该方法做了限制。如果是普通调用的话,它会抛出一个SecurityException异常。只有由系统类加载器(BootStrap classLoader)加载的类才可以调用这个类中的方法。
如果var0由系统类加载器加载的话,那么var0.getClassLoader()会返回null,VM.isSystemDomainLoader(null)则直接返回true,此时便不会抛出SecurityException异常。
当然,也不是无法获取到Unsafe类的实例,我们在文章最后会通过反射来获取。
public native long staticFieldOffset(Field var1);
public native long objectFieldOffset(Field var1);
偏移量这个名词在Unsafe类中十分重要,该类中80%的方法都需要依赖这个偏移量。
//分配内存
public native long allocateMemory(long var1);
//扩展或重新分配内存
public native long reallocateMemory(long var1, long var3);
//内存初始化
public native void setMemory(Object var1, long var2, long var4, byte var6);
//内存复制
public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);
//释放内存
public native void freeMemory(long var1);
//创建对象实例并返回该实例
public native Object allocateInstance(Class<?> var1) throws InstantiationException;
其中allocateMemory()、reallocateMemory()、freeMemory()分别用于分配内存,扩展或重新分配内存和释放内存,与C语言中?malloc()、realloc()、free()对应。
allocateInstance()方法会通过Class对象创建一个类的实例,且不需要调用其构造函数、初始化代码、JVM安全检查等等。
public native int getInt(long var1);
public native void putInt(long var1, int var3);
普通的读写,无法保证有序性与可见性。关于可见性,可以先移步到我的另外一篇文章多线程之内存可见性
public native int getIntVolatile(Object var1, long var2);
public native void putIntVolatile(Object var1, long var2, int var4);
volatie能够保证有序性以及可见性,volatie保证有序性的一个实例,可以参考我的另外一篇文章浅说Synchronized中使用synchronized与volatie实现单例模式中双重检验锁的部分。
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
以compareAndSwapInt为例,在对象var1指定偏移量var2处读取一个int值real,如果var4=real,则用var5更新这个real,并返回true,否则返回false。
CAS用于实现乐观锁,每次更新时,都假设不会有其他线程并发修改,而只是在修改的时候判断该值是否被修改过。如果符合预期的值,则直接更新它,否则进入忙循环中,一直判断是否符合期望。在循环次数少便可以直接更新值的情况下,CAS机制比悲观锁拥有更好的性能。当然,如果循环次数过多,也是会白白浪费CPU资源。
synchronized就是一中悲观锁的实现,关于synchronized原理,可以移步我的另外一篇文章Synchronized的优化
关于CAS机制的详细介绍,我会另开篇幅。
public native void park(boolean var1, long var2);
public native void unpark(Object var1);
LockSupport里面的park()与unpark()方法内部正是通过以上两个本地方法来实现的。
public native void loadFence();
public native void storeFence();
public native void fullFence();
package com.yang.testUnsafe;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class Main {
static class Student {
private String name;
private int age;
public Student() {
System.out.println("通过构造方法");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
//1.获取Unsafe类的实例
//但无法通过Unsafe.getUnsafe(),由于Main类不是系统类加载器加载,因此会抛出SecurityException异常
//Unsafe unsafe = Unsafe.getUnsafe();
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
//2.为Student对象分配内存
//该方法不需要调用构造方法
Student student = (Student) unsafe.allocateInstance(Student.class);
//3.获取student实例中age属性的偏移量
Field age = student.getClass().getDeclaredField("age");
long ageOffset = unsafe.objectFieldOffset(age);
//4.利用CAS操作,当age=0时,将age变为1
boolean casResult = unsafe.compareAndSwapInt(student, ageOffset, 0, 1);
System.out.println(casResult);//输出true
System.out.println(student.getAge());//输出1
}
}
使用Unsafe类直接操纵内存,意味着速度更快,效率更高,但也更加危险。之前盛传Unsafe类将在Java9中移除,一时间风波四起,具体的文章可以参考这篇Java 9中将移除 Sun.misc.Unsafe(译)。
但其实Java9出现之后,只是对其进行了改进和优化,不过依然是不推荐开发者使用Unsafe类。
cs