Unsafe这个神奇的类

这个类啊是在并发包里很多类都用到的一个类,打开一看,莫名其妙,全是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的这些方法去保证他们关键字段操作的线程安全而已,方法就那些,所以大家就散了吧~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。