【Java】Unsafe源码走读及实战应用

前言

  • Unsafe顾名思义,它不安全,要小心使用
  • Unsafe可以控制对象的内存申请和释放,可以对内存访问进行控制
  • Unsafe本身仅是为JDK服务的,不推荐应用程序直接使用,且JDK可能随时会改动它

以下演示的JDK版本:1.8

1 使用

部分源码:

Unsafe:
//私有变量
private static final Unsafe theUnsafe;

//私有构造函数
private Unsafe(){}

//私有变量初始化
static {
	......
	theUnsafe = new Unsafe();
	......
}

//公开获取Unsafe对象
public static Unsafe getUnsafe() {
	Class arg = Reflection.getCallerClass();
	//判断当前ClassLoader是否是顶层类加载器(null),所以一般情况下,只有rt.jar中的类可以使用Unsafe对象
	if (!VM.isSystemDomainLoader(arg.getClassLoader())) {
	    throw new SecurityException("Unsafe");
	} else {
	    return theUnsafe;
	}
}
VM:
//判断ClassLoader是否是顶层类加载器(null)
public static boolean isSystemDomainLoader(ClassLoader arg) {
	return arg == null;
}

分析:

  • Unsafe类是final类型,无法被其他类继承
  • Unsafe构造函数为私有,不能通过常规途径创建对象
  • Unsafe成员变量theUnsafe为私有,无法被外部访问
  • 唯一的公开方法getUnsafe,限制了只有rt.jar才能使用(或者是-Xbootclasspath指定class)

那咋办?只能靠反射了,通过构造函数或成员变量,都能获取Unsafe对象:

/**
* 通过私有构造函数Unsafe()获取Unsafe
* 
* @return
* @throws Exception
*/
public static Unsafe getUnsafeByContructor() throws Exception {
	// 获取私有无参构造函数Unsafe()
	Constructor<Unsafe> constructor = Unsafe.class.getDeclaredConstructor();
	// 关闭安全检查,不代表一定可以访问,但一定可以减小安全检查的资源消耗,常用于反射类功能中
	constructor.setAccessible(true);
	Unsafe unsafe = (Unsafe) constructor.newInstance();
	return unsafe;
}
/**
* 通过私有成员变量theUnsafe获取Unsafe对象
* 
* @return
* @throws Exception
*/
public static Unsafe getUnsafeByField() throws Exception {
	// 获取私有成员变量theUnsafe
	Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
	// 关闭安全检查,不代表一定可以访问,但一定可以减小安全检查的资源消耗,常用于反射类功能中
	unsafeField.setAccessible(true);
	Unsafe unsafe = (Unsafe) unsafeField.get(null);
	return unsafe;
}

2 并发安全

2.1 CAS

一种可以支持并发修改的判断:

//1:待更新对象;2:内存地址-偏移量;3:当前预期值;4:待更新值;
//更新成功返回true,否则为false
public final native boolean compareAndSwapObject(Object arg0, long arg1, Object arg3, Object arg4);
public final native boolean compareAndSwapInt(Object arg0, long arg1, int arg3, int arg4);
public final native boolean compareAndSwapLong(Object arg0, long arg1, long arg3, long arg5);

2.2 volatile

Object类型和8种基础数据类型的18个Get和Put方法,包含volatile特性:工作内存中的值永远最新;禁止指令重排。

public native Object getObjectVolatile(Object arg0, long arg1);
public native void putObjectVolatile(Object arg0, long arg1, Object arg3);
public native int getIntVolatile(Object arg0, long arg1);
public native void putIntVolatile(Object arg0, long arg1, int arg3);
public native boolean getBooleanVolatile(Object arg0, long arg1);
public native void putBooleanVolatile(Object arg0, long arg1, boolean arg3);
public native byte getByteVolatile(Object arg0, long arg1);
public native void putByteVolatile(Object arg0, long arg1, byte arg3);
public native short getShortVolatile(Object arg0, long arg1);
public native void putShortVolatile(Object arg0, long arg1, short arg3);
public native char getCharVolatile(Object arg0, long arg1);
public native void putCharVolatile(Object arg0, long arg1, char arg3);
public native long getLongVolatile(Object arg0, long arg1);
public native void putLongVolatile(Object arg0, long arg1, long arg3);
public native float getFloatVolatile(Object arg0, long arg1);
public native void putFloatVolatile(Object arg0, long arg1, float arg3);
public native double getDoubleVolatile(Object arg0, long arg1);
public native void putDoubleVolatile(Object arg0, long arg1, double arg3);

2.3 CAS+Volatile

//给对象增加指定值:1-对象;2-内存地址;3-增加值
public final int getAndAddInt(Object arg0, long arg1, int arg3) {
	int arg4;
	do {
	    arg4 = this.getIntVolatile(arg0, arg1);
	} while (!this.compareAndSwapInt(arg0, arg1, arg4, arg4 + arg3));

	return arg4;
}

public final long getAndAddLong(Object arg0, long arg1, long arg3) {
	......
}
//给对象赋予指定值:1-对象;2-内存地址;3-赋予值
public final int getAndSetInt(Object arg0, long arg1, int arg3) {
	int arg4;
	do {
	    arg4 = this.getIntVolatile(arg0, arg1);
	} while (!this.compareAndSwapInt(arg0, arg1, arg4, arg3));

	return arg4;
}

public final long getAndSetLong(Object arg0, long arg1, long arg3) {
	......
}

public final Object getAndSetObject(Object arg0, long arg1, Object arg3) {
	......
}

2.4 内存屏障

//内存屏障,禁止load操作重排序,即屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前
//即a=1;loadFence();b=2;那么工作内存中一定是先发生a=1,然后再发生b=2;
public native void loadFence();

//内存屏障,禁止store操作重排序,即屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前
//即a=1;storeFence();b=2;那么主内存中一定是先发生a=1,然后再发生b=2;工作内存中,可能先b=2,后a=1;
public native void storeFence();

//内存屏障,禁止load、store操作重排序
//即a=1;fullFence();b=2;那么工作内存和主内存都是先发生a=1,然后再发生b=2;
public native void fullFence();

volatile和内存屏障不了解的可以看:java内存模型与线程

2.5 有序写入

//设置对象var1中offset偏移地址var2对应的Object型field的值为指定值var4
public native void putOrderedObject(Object var1, long var2, Object var4);

public native void putOrderedInt(Object var1, long var2, int var4);

public native void putOrderedLong(Object var1, long var2, long var4);

3 其他功能

仅了解即可,用到或见到时候便于查看。

3.1 非堆内存操作

//获得本地指针
public native long getAddress(long address);
//存储本地指针到给定的内存地址
public native void putAddress(long address, long x);
  
//分配内存
public native long allocateMemory(long bytes);
//重新分配内存
public native long reallocateMemory(long address, long bytes);

//初始化内存内容
public native void setMemory(Object o, long offset, long bytes, byte value);
public void setMemory(long address, long bytes, byte value) {
 setMemory(null, address, bytes, value);
}

//内存内容拷贝
public native void copyMemory(Object srcBase, long srcOffset,Object destBase, long destOffset,long bytes);
public void copyMemory(long srcAddress, long destAddress, long bytes) {
 copyMemory(null, srcAddress, null, destAddress, bytes);
}

//释放内存
public native void freeMemory(long address)

//返回指针的大小,返回值为4或8
public native int addressSize();
//内存页的大小
public native int pageSize();  

3.2 偏移量操作

//静态属性存储分配中的位置(偏移地址)。
public native long staticFieldOffset(Field arg0);

//非静态属性存储分配中的位置(偏移地址)
public native long objectFieldOffset(Field arg0);

//返回给定的静态属性的位置,配合staticFieldOffset方法使用。返回值是静态属性所在的Class对象的一个内存快照
//例:unsafe.getObject(unsafe.staticFieldBase(name), unsafe.staticFieldOffset(name))
public native Object staticFieldBase(Field arg0);

//返回数组中第一个元素实际地址相对整个数组对象的地址的偏移量
public native int arrayBaseOffset(Class<?> var1);

//返回数组中第一个元素所占用的内存空间
public native int arrayIndexScale(Class<?> var1);

3.3 对象操作

类似2.2volatile数据操作,仅是没有volatile特性,同样有18个方法,仅展示int类型的:

//获得给定地址上的int值
public native int getInt(long address);
//设置给定地址上的int值
public native void putInt(long address, int x);

3.4 线程控制

//取消阻塞线程
public native void unpark(Object thread);
//阻塞线程
public native void park(boolean isAbsolute, long time);

3.5 Class操作

//检测给定的类是否需要初始化。使用在获取一个类的静态属性的时候(因为一个类如果没初始化,它的静态属性也不会初始化)
public native boolean shouldBeInitialized(Class<?> arg0);

//检测给定的类是否已经初始化,使用场景同上
public native void ensureClassInitialized(Class<?> arg0);

//定义一个类,返回类实例,此方法会跳过JVM的所有安全检查。
public native Class<?> defineClass(String name, byte[] b, int off, int len,ClassLoader loader,ProtectionDomain protectionDomain);

//定义一个匿名类,可用于动态创建类
public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches);

//创建一个类的实例,但不会调用这个实例的构造方法,如果这个类还未被初始化,则初始化这个类
public native Object allocateInstance(Class<?> var1) throws InstantiationException;

4 实现自定义原子类

直接看代码:

public class CASTest {

    public static CountDownLatch latch;

    public static void main(String[] args) throws Exception {
        CASInteger CASInteger = new CASInteger(0);
        Runnable casAdd = () -> {
            //每个线程修改1万次
            for(int i = 0; i < 10000; i++){
                CASInteger.casIncreament();
            }
            latch.countDown();
        };
        //线程数量100个
        int threadNum = 100;
        latch = new CountDownLatch(threadNum);
        long start = System.currentTimeMillis();
        //启动所有线程
        for(int i = 0; i < threadNum; i++){
            new Thread(casAdd).start();
        }
        //等待所有线程结束,
        latch.await();
        System.out.println("time cost : " + (System.currentTimeMillis() - start)+" ms");
        System.out.println("result: " + CASInteger.getValue());
    }
}

/**
 * 自定义CAS-Integer,Thread Safe
 */
class CASInteger {
    private sun.misc.Unsafe unsafe;
    private long offset;
    private volatile int value ;

    public CASInteger(int value) throws Exception {
        this.value = value;
        //获取Unsafe对象
        unsafe = getUnsafeByContructor();
        //当前对象的偏移量
        offset = unsafe.objectFieldOffset(CASInteger.class.getDeclaredField("value"));
    }

    /**
     * 安全自增,仅当更新值比当前对象值多1时,才能更新成功。
     */
    public void casIncreament(){
        //效果等价于:unsafe.getAndAddInt(this, offset, 1);
        while (!unsafe.compareAndSwapInt(this, offset, value, value + 1)){
        }
    }

    public int getValue() {
        return value;
    }

    /**
     * 通过私有构造函数Unsafe()获取Unsafe
     *
     * @return
     * @throws Exception
     */
    private static Unsafe getUnsafeByContructor() throws Exception {
        // 获取私有无参构造函数Unsafe()
        Constructor<Unsafe> constructor = Unsafe.class.getDeclaredConstructor();
        // 关闭安全检查,不代表一定可以访问,但一定可以减小安全检查的资源消耗,常用于反射类功能中
        constructor.setAccessible(true);
        Unsafe unsafe = (Unsafe) constructor.newInstance();
        return unsafe;
    }
}

结果:146毫秒,自增100万次,数据正确

time cost : 113 ms
result: 1000000

PS:value类型是volatile的int类型,为了保证数据可见;compareAndSwapInt传参必须是int,不能是Integer,不然可能会内存奔溃哦。

 

 


爱家人,爱生活,爱设计,爱编程,拥抱精彩人生!

发布了94 篇原创文章 · 获赞 232 · 访问量 11万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章