直接调用Unsafe中的getUnsafe()的方法获取Unsafe时候是不会报错的,但是运行的时候就会报错,如下:
进入getUnsafe()方法中:
var0是当前调用当前方法的类的class,var0.getClassLoader()方法获取的是当前类的加载器AppClassLoader:
获取出加载器的时候进入方法isSystemDomainLoader(ClassLoader var0)进行判断,这个判断返回是false,则getsafe()方法中的判断则是true,则抛出异常:
所以只能用BootstrapClassLoader加载器中调用的类使用这个unsage,比如concurrenthashmap是在BootstrapClassLoader中加载的,getClassLoader0()获取出来是为null的,所以不会抛出异常。因为Unsafe中有个theUnsafe的属性,所以可以通过放射来获取这个已经实例化的对象属性,从而获得unsafe对象:
/**
* Unsafe 类是直接操作操作系统的,所以是不安全的类,不允许自己new
*/
public class UnSafeTest {
private int i = 0;
private static Unsafe unsafe;
//偏移量,unsafe方法中会使用到
private static long COUNT_OFFSET;
static {
//不能直接new,会报错
//unsafe = Unsafe.getUnsafe();
//Unsafe类中有一个熟悉private static final Unsafe theUnsafe; 内部通过new实例化了,所以
// 可以通过反射类获取这个属性,从而获取unsfe
try {
//获取属性
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
//获取i属性字段的偏移量
COUNT_OFFSET = unsafe.objectFieldOffset(UnSafeTest.class.getDeclaredField("i"));
} catch (IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
UnSafeTest unSafeTest = new UnSafeTest();
//创建两个线程来操作变量i
new Thread(new Runnable() {
@Override
public void run() {
//修改unsafe对象中偏移量为COUNT_OFFSET属性的值,如果unSafeTest.i跟内存中的值一致,说明还没被修改
//则进行加1操作,如果不一致,则不进行操作
boolean b = unsafe.compareAndSwapInt(unSafeTest, COUNT_OFFSET, unSafeTest.i, unSafeTest.i + 1);
if (b){
System.out.println(unsafe.getIntVolatile(unSafeTest, COUNT_OFFSET));
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
boolean b = unsafe.compareAndSwapInt(unSafeTest, COUNT_OFFSET, unSafeTest.i, unSafeTest.i + 1);
if (b){
System.out.println(unsafe.getIntVolatile(unSafeTest, COUNT_OFFSET));
}
}
}).start();
}
}