这个类啊是在并发包里很多类都用到的一个类,打开一看,莫名其妙,全是native定义的方法,作用是啥,怎么用?一堆问题,慢慢看了几篇文章,有了滴滴头绪~
听这个名字就知道,这是一个不安全的类,而且由于它设置了保护机制,我们一般的类是没办法使用它的(也不是真的没办法,好多地方也悄悄用了,只是正常情况下没办法使用),所以我们就了解一下就是了,它是怎么拒绝一般的类使用它的呢?
首先,它只有一个private的构造函数,然后它自己内部维护了一个自身的实例,想要获取这个实例的话,有如下方式:
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
VM.isSystemDomainLoader(var0.getClassLoader())这个方法呢就是去检查调用次方法的类的类加载器是否是bootstrap classloader,如果不是的话,就抛异常,大家都知道,bootstrap classloader它负责加载Java的核心类,我们一般的类用的是系统类加载器,所以这里就没法继续了(再次申明,正常情况没法继续,有一些黑方法是可以的,但是我不想展开讲,因为又要写一大堆,如果想知道就留言,有人想看我再写~),这个类调用的都是系统本地接口,其他语言实现的,我们这儿在源码里也看不到(想看也能找到),所以洗洗睡吧~
咳咳,然后这个Unsafe类的作用是什么呢?
可多了,每个方法都有自己的作用(可能有些人想打我=。=)
So,举几个例子吧(从国外的文章搬过来给小伙伴们欣赏https://dzone.com/articles/understanding-sunmiscunsafe)
首先它能在不通过调用构造函数的情况下创建一个对象实例:
class ClassWithExpensiveConstructor {
private final int value;
private ClassWithExpensiveConstructor() {
value = doExpensiveLookup();
}
private int doExpensiveLookup() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 1;
}
public int getValue() {
return value;
}
}
@Test
public void testObjectCreation() throws Exception {
ClassWithExpensiveConstructor instance = (ClassWithExpensiveConstructor)
unsafe.allocateInstance(ClassWithExpensiveConstructor.class);
assertEquals(0, instance.getValue());
}
可以看到,通过allocateInstance这个方法就这么生成了一个实例instance,炒鸡方便有木有(其实并没有什么了不起,反射完爆一切~)。好吧,这篇文章展示了这么一个方法,什么时候用到也未知,大概率你一辈子也不会用到。。。
后面这个功能呢,就要牛逼很多了,就是分配内存!大家都知道,java里面呢,有那么几个原始的数据类型,大小呢也是固定的,比如int就4个字节,最大值也就Integer.MAX_VALUE,
那么,我们想要那么一个数字,最大值是超越了Integer.MAX_VALUE的话就要想别的方法啦(别说这种情况很少遇到,凡事都有例外呢~)
那么恰好呢,这个Unsafe类就提供了那么些方法,我们来看个例子:
class DirectIntArray {
private final static long INT_SIZE_IN_BYTES = 4;
private final long startIndex;
public DirectIntArray(long size) {
startIndex = unsafe.allocateMemory(size * INT_SIZE_IN_BYTES);
unsafe.setMemory(startIndex, size * INT_SIZE_IN_BYTES, (byte) 0);
}
}
public void setValue(long index, int value) {
unsafe.putInt(index(index), value);
}
public int getValue(long index) {
return unsafe.getInt(index(index));
}
private long index(long offset) {
return startIndex + offset * INT_SIZE_IN_BYTES;
}
public void destroy() {
unsafe.freeMemory(startIndex);
}
}
@Test
public void testDirectIntArray() throws Exception {
long maximum = Integer.MAX_VALUE + 1L;
DirectIntArray directIntArray = new DirectIntArray(maximum);
directIntArray.setValue(0L, 10);
directIntArray.setValue(maximum, 20);
assertEquals(10, directIntArray.getValue(0L));
assertEquals(20, directIntArray.getValue(maximum));
directIntArray.destroy();
}
通过allocateMemory方法,就可以分配相应字节的内存啦,然后再通过unsafe.setMemory就可以给分配好的内存赋值,假如你想按着int类型的数据也就是4个字节4个字节赋值可以调用unsafe.putInt,这个是不是还蛮有用的?
在开头我说了,并发包里好多类在对于关键字段的修改上都用了Unsafe类的方法,所以,关键的来了哦,就是以下一些方法:
public final native booleancompareAndSwapObject(Object var1, longvar2,Object var4,Object var5);
public final native booleancompareAndSwapInt(Object var1, longvar2, intvar4, intvar5);
public final native booleancompareAndSwapLong(Object var1, longvar2, longvar4, longvar6);
看名字就知道,跟CAS脱不了关系啦,以及还有一些这种方法:
public native intgetIntVolatile(Object var1, longvar2);
public native voidputIntVolatile(Object var1, longvar2, intvar4);
看名字就知道,和Volatile也有关系啦,不知道CAS和Volatile的去面壁吧~
所以说呢,并发包就是用Unsafe的这些方法去保证他们关键字段操作的线程安全而已,方法就那些,所以大家就散了吧~
网友评论