版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_25673113/article/details/79107333
Unsafe
Unsafe 是sun.misc.Unsafe
下的一个包,通过这个类可以直接使用底层native方法来获取和操作底层的数据,例如获取一个字段在内存中的偏移量,利用偏移量直接获取或修改一个字段的数据等等……
当然这个类正如他的名字一样:不安全的操作。如何理解这个不安全呢?在java的世界里所有的变量都是通过把代码编译成class字节码加载到JVM虚拟机中,通过虚拟机来操作内存中字段的数据,在此过程中,JVM帮程序员做了很多事情,其中大部分是内存的管理。
在JUC(java.util.concurrent)中使用了大量的Unsafe类(多线程、concurrentHashMap、AtomicInteger等等),他可以很方便的使用CAS
来确保数据的可见性,同时比正常的Synchroized速度快很多。
使用Unsafe
private Unsafe() {}
//获取示例
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
//必须使用BootClassLoader 加载类
if(!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
因为构造方法私有化,导致不能手动创建实例化,也就是我们平时所说的单例模式。我们可以从Unsafe类中的getUnsafe
获取实例,但是返回theUnsafe实例前,会对类加载器进行判断,当不为BootClassLoader
时,会抛出错误。
有两种解决方法:
- 当运行你的代码时使用bootclasspath选项并且指定你使用Unsafe类的路径到系统类中。(没有试过,我猜是用BootClassLoader来加载这个类,因为BootClassLoader默认加载rt.jar下的class文件)
java -Xbootclasspath:/usr/jdk1.7.0/jre/lib/rt.jar:
你的类名
- 通过反射获取类中的
theUnsafe
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
U = (Unsafe) field.get(null);
当获取Unsafe单例后就可以正常使用了CAS了。传送门:JAVA CAS原理深度分析
package day5;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
/**
* @author luokai
* @date 2018/1/15
*/
public class UnsafeTest {
// Unsafe mechanics
private static final sun.misc.Unsafe U;
private static final long SIZE;
private transient volatile int size;
static {
try {
//系统对Unsafe.getUnsafe 的调用有限制,类加载器必须的限制,但是我们可以使用反射进行获取
//U = sun.misc.Unsafe.getUnsafe();
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
U = (Unsafe) field.get(null);
//获取size偏移量
SIZE = U.objectFieldOffset
(UnsafeTest.class.getDeclaredField("size"));
} catch (Exception e) {
throw new Error(e);
}
}
public static void main(String[] args) {
UnsafeTest unsafeTest = new UnsafeTest();
unsafeTest.size = 2;
//CAS方法
System.out.println(U.compareAndSwapInt(unsafeTest, SIZE, 123, -1)); //失败
System.out.println(U.compareAndSwapInt(unsafeTest, SIZE, unsafeTest.size, -1));
System.out.println(unsafeTest.size);
}
}