java——Unsafe

JAVA如何实现CAS?

前一阵子在看AQS的时候发现用了很多CAS操作,毕竟AQS本身是没有进行显示的上锁,采用CAS同步无可厚非。那CAS是如何实现的呢?在JAVA中CAS调用的方法是Unsafe类的native方法。

Unsafe类是在sun.misc包下,不属于Java标准。但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty、Cassandra、Hadoop、Kafka等。Unsafe类在提升Java运行效率,增强Java语言底层操作能力方面起了很大的作用。

Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力,同时也带来了指针的问题。过度的使用Unsafe类会使得出错的几率变大,因此Java官方并不建议使用的,官方文档也几乎没有。Oracle正在计划从Java 9中去掉Unsafe类。

Unsafe能做什么?

推荐去看源代码,但是源代码中没有注释也没有文档而且方法基本都是native,比较难懂,这里做个总结。

内存管理

该部分包括了allocateMemory(分配内存)、reallocateMemory(重新分配内存)、copyMemory(拷贝内存)、freeMemory(释放内存 )、getAddress(获取内存地址)、addressSize、pageSize、getInt(获取内存地址指向的整数)、getIntVolatile(获取内存地址指向的整数,并支持volatile语义)、putInt(将整数写入指定内存地址)、putIntVolatile(将整数写入指定内存地址,并支持volatile语义)、putOrderedInt(将整数写入指定内存地址、有序或者有延迟的方法)等方法。getXXX和putXXX包含了各种基本类型的操作。

特殊的对象实例化

allocateInstance()方法提供了另一种创建实例的途径。通常我们可以用new或者反射来实例化对象,使用allocateInstance()方法可以直接生成对象实例,且无需调用构造方法和其它初始化方法。这在对象反序列化的时候会很有用,能够重建和设置final字段,而不需要调用构造方法。

操作类、对象、变量

这部分包括了staticFieldOffset(静态域偏移)、defineClass(定义类)、defineAnonymousClass(定义匿名类)、ensureClassInitialized(确保类初始化)、objectFieldOffset(对象域偏移)等方法。

通过这些方法我们可以获取对象的指针,通过对指针进行偏移,我们不仅可以直接修改指针指向的数据(即使它们是私有的),甚至可以找到JVM已经认定为垃圾、可以进行回收的对象。

数组操作

这部分包括了arrayBaseOffset(获取数组第一个元素的偏移地址)、arrayIndexScale(获取数组中元素的增量地址)等方法。arrayBaseOffset与arrayIndexScale配合起来使用,就可以定位数组中每个元素在内存中的位置。

由于Java的数组最大值为Integer.MAX_VALUE,使用Unsafe类的内存分配方法可以实现超大数组。实际上这样的数据就可以认为是C数组,因此需要注意在合适的时间释放内存。

多线程同步

Unsafe类的CAS操作可能是用的最多的,它为Java的锁机制提供了一种新的解决办法,比如AtomicInteger等类都是通过该方法来实现的。compareAndSwap方法是原子的,可以避免繁重的锁机制,提高代码效率。这是一种乐观锁,通常认为在大部分情况下不出现竞态条件,如果操作失败,会不断重试直到成功。

挂起与恢复

这部分包括了park、unpark等方法。

将一个线程进行挂起是通过park方法实现的,调用 park后,线程将一直阻塞直到超时或者中断等条件出现。unpark可以终止一个挂起的线程,使其恢复正常。整个并发框架中对线程的挂起操作被封装在 LockSupport类中,LockSupport类中有各种版本pack方法,但最终都调用了Unsafe.park()方法。

内存屏障

这部分包括了loadFence、storeFence、fullFence等方法。这是在Java 8新引入的,用于定义内存屏障,避免代码重排序。

loadFence() 表示该方法之前的所有load操作在内存屏障之前完成。同理storeFence()表示该方法之前的所有store操作在内存屏障之前完成。fullFence()表示该方法之前的所有load、store操作在内存屏障之前完成。

我们能使用Unsafe么?

Unsafe有很多基层的操作内存的方法类似于c,如果我们能够使用它就可以获得更多的操作空间。那么我们能使用Unsafe么?

我们是不能使用Unsafe的,1.8版本。

  1. 首先Unsafe在sun.misc包中,是一个单例模式,如果我们如果想要使用的话需要申请获得这个单例静态对象。
  2. 在获取对象的时候会有个判断,判断当前的类加载器,如果不是bootstrap的话就不能继续进行。
  3. 对于这种情况我们可以设置classpath进行模拟类加载器的路径,或者利用反射方法直接获取。
  4. Unsafe的单例模式有@CallerSensitive标记,会对非调用方法者进行鉴定,判断调用方法的人的权限,这个注解在1.8才被加入,有了这个注解,我们就不能通过这样的方法来调用,最后会识别出我们的类加载器类型是父加载器的子类,权限不够。

猜你喜欢

转载自blog.csdn.net/QuinnNorris/article/details/81096983