java中Unsafe类函数功能介绍

  • Post author:
  • Post category:java




Unsafe类功能

java不能直接访问操作系统底层,而是通过本地方法来访问,Unsafe类提供了硬件级别的原子操作,主要提供了以下功能:

  • 1、通过Unsafe类可以分配内存,可以释放内存
  • 2、可以定位对象某字段的内存位置,也可以修改对象的字段值,就算是它是私有的
  • 3、挂起与恢复线程
  • 4、CAS操作



分配内存和释放内存

Unsafe类中提供的3个本地方法

allocateMemory



reallocateMemory



freeMemory

分别用于

分配内存



扩充内存



释放内存

,与C语言中的3个方法对应。

public native long allocateMemory(long var1);
public native void freeMemory(long var1);
 public native Object allocateInstance(Class<?> var1) throws InstantiationException;



定位对象某字段的内存位置以及修改对象的字段值


字段的定位




staticFieldOffset()

方法可以获取对象字段的偏移地址,该方法返回给定Field的内存地址偏移量,这个值对于给定的Filed是唯一的且是固定不变的。

public native long staticFieldOffset(Field var1);

Unsafe类中提供很多getxxx方法,就是根据对象地址和字段偏移地址来获取某个字段对应的值,由于字段的类型很多(

byte,short,int,long,float,float,double,char,boolean,引用数据类型等

),所以这种方法也就有很多种,线程安全使用使用

getxxxVolatile()

方法,否则使用

getxxx()

方法,

下面就使用获取int类型的字段的值举例


  • getInt()

    方法可以获取对象中offset偏移地址对应的

    整型Field

    的值。

    /**
    * 功能:获取的是整形字段的值 int
    * Object var1:包含要读取字段(Field)的对象
    * long var2:该字段的偏移地址
    * */
    public native int getInt(Object var1, long var2);;
    

  • getLongVolatile()

    方法获取对象中offset偏移地址对应的

    整型Field

    的值,

    支持volatile load语义

    /**
    * 功能:获取的是整形字段的值 long
    * Object var1:包含要读取字段(Field)的对象
    * long var2:该字段的偏移地址
    * */
    public native long getIntVolatile(Object var1, long var2);
    

注意:Unsafe类中还提供了一个形参的

getxxx

方法,没有提供一个形参的

getxxxVolatile()

方法,例如:

 public native int getInt(long var1);


数组元素定位



Unsafe类中有很多以

BASE_OFFSET

结尾的常量,比如ARRAY_INT_BASE_OFFSET,ARRAY_BYTE_BASE_OFFSET等,这些常量值是通过arrayBaseOffset()方法得到的。

arrayBaseOffset()

方法是一个本地方法,可以获取数组第一个元素的偏移地址。

Unsafe类中还有很多以

INDEX_SCALE

结尾的常量,比如 ARRAY_INT_INDEX_SCALE , ARRAY_BYTE_INDEX_SCALE等,这些常量值是通过arrayIndexScale方法得到的。

arrayIndexScale()

方法也是一个本地方法,可以获取数组的转换因子,也就是数组中元素的增量地址。将arrayBaseOffset与arrayIndexScale配合使用,可以定位数组中每个元素在内存中的位置。

public final class Unsafe {
    private static final Unsafe theUnsafe;
    public static final int INVALID_FIELD_OFFSET = -1;
    public static final int ARRAY_BOOLEAN_BASE_OFFSET;
    public static final int ARRAY_BYTE_BASE_OFFSET;
    public static final int ARRAY_SHORT_BASE_OFFSET;
    public static final int ARRAY_CHAR_BASE_OFFSET;
    public static final int ARRAY_INT_BASE_OFFSET;
    public static final int ARRAY_LONG_BASE_OFFSET;
    public static final int ARRAY_FLOAT_BASE_OFFSET;
    public static final int ARRAY_DOUBLE_BASE_OFFSET;
    public static final int ARRAY_OBJECT_BASE_OFFSET;
    public static final int ARRAY_BOOLEAN_INDEX_SCALE;
    public static final int ARRAY_BYTE_INDEX_SCALE;
    public static final int ARRAY_SHORT_INDEX_SCALE;
    public static final int ARRAY_CHAR_INDEX_SCALE;
    public static final int ARRAY_INT_INDEX_SCALE;
    public static final int ARRAY_LONG_INDEX_SCALE;
    public static final int ARRAY_FLOAT_INDEX_SCALE;
    public static final int ARRAY_DOUBLE_INDEX_SCALE;
    public static final int ARRAY_OBJECT_INDEX_SCALE;
    public static final int ADDRESS_SIZE;
    
    public native int arrayBaseOffset(Class<?> var1);
    public native int arrayIndexScale(Class<?> var1);



挂起与恢复线程

将一个线程进行挂起是通过

park

方法实现的,调用

park

后,线程将一直阻塞直到超时或者中断等条件出现

,unpark

可以终止一个挂起的线程,使其恢复正常。

public native void park(boolean var1, long var2);
public native void unpark(Object var1);

整个并发框架中对线程的挂起操作被封装在

LockSupport

类中,该类是JUC包下的一个类,LockSupport类中有各种版本pack方法,但最终都调用了

Unsafe.park()

方法。



CAS操作

CAS是通过compareAndSwapXXX方法实现的,如下:


  • compareAndSwapObject

  • compareAndSwapInt

  • compareAndSwapLong
/**
* 比较obj的offset处内存位置中的对象和期望对象,如果相同则更新,此更新是不可中断的。
* 
* @param var1 需要更新的对象
* @param var2 obj中obj字段的偏移量,注意obj是一种通称
* @param var4 希望obj字段的值
* @param var5 如果期望值var4与var2相同,设置obj字段的值为这个新值
* @return 如果obj字段的值被更改返回true
*/
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);

/**
* 比较obj的offset处内存位置中的值和期望的值,如果相同则更新,此更新是不可中断的。
* 
* @param obj 需要更新的对象
* @param offset obj中整型field的偏移量
* @param expect 希望field中存在的值
* @param update 如果期望值expect与field的当前值相同,设置filed的值为这个新值
* @return 如果field的值被更改返回true
*/
public final native boolean compareAndSwapInt(Object obj, long offset, int expect, int update);

public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

CAS 操作包含三个操作数 —— 内存位置(offset)、预期原值(expect)和新值(update),如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作

Java并发包

(java.util.concurrent

)中大量使用了CAS操作,涉及到并发的地方都调用了

sun.misc.Unsafe

类方法进行CAS操作。



Unsafe类功中其他方法介绍

  • 注意:这里虽然只是介绍了部分方法,但是大多数的方法使用是相同的,只不过是对不同数据类型进行操作而已!!!
//下面是sun.misc.Unsafe.java类源码
package sun.misc;
import java.lang.reflect.Field;
/***
 * 这个类提供了一个更底层的操作并且应该在受信任的代码中使用,可以通过内存地址
 * 存取fields,如果给出的内存地址是无效的那么会有一个不确定的运行表现。
 */
public class Unsafe
{
  // 单例模式,对外不提供构造方法,使用的时候,使用对用的getxxx方法获取该对象实例
  private static Unsafe unsafe = new Unsafe();
  /***
   * 构造器私有
   */
  private Unsafe(){}
  /**
   * 获取Unsafe的单例
   */
  public static Unsafe getUnsafe()
  {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null)
      sm.checkPropertiesAccess();
    return unsafe;
  }
  
  /***
   * 返回指定静态字段(Field)的内存地址偏移量
   * 这个偏移量对于给定的Field是唯一的,并且后续对该方法的调用都应该返回相同的值。
   *
   * @param field 需要返回偏移量的Field
   * @return Field的偏移量
   */
  public native long objectFieldOffset(Field field);
  /**
   * 在obj的offset位置比较integer field和期望的值,如果相同则更新。这个方法
   * 的操作应该是原子的,因此提供了一种不可中断的方式更新integer field。
   * 
   * @param obj 包含要修改field的对象
   * @param offset obj中整型field的偏移量
   * @param 希望field中存在的值
   * @param 如果期望值expect与field的当前值相同,设置filed的值为这个新值
   * @return true 如果field的值被更改
   */
  public native boolean compareAndSwapInt(Object obj, long offset,
                                          int expect, int update);
  /**
   * 参考 compareAndSwapInt()
   */
  public native boolean compareAndSwapLong(Object obj, long offset,
                                           long expect, long update);
  /***
   * 在obj的offset位置比较object field和期望的值,如果相同则更新。这个方法
   * 的操作应该是原子的,因此提供了一种不可中断的方式更新object field。
   * 
   * @param 包含要修改field的对象 
   * @param obj中object型field的偏移量
   * @param 希望field中存在的值
   * @param 如果期望值expect与field的当前值相同,设置filed的值为这个新值
   * @return true 如果field的值被更改
   */
  public native boolean compareAndSwapObject(Object obj, long offset,
                                             Object expect, Object update);
  /***
   * 设置obj对象中offset偏移地址对应的整型field的值为指定值。这是一个有序或者
   * 有延迟的putIntVolatile方法,并且不保证值的改变被其他线程立即看到。
   * 只有在field被volatile关键字修饰并且期望被意外修改的时候使用才有用。
   *
   * @param 包含需要修改field的对象
   * @param obj中整型field的偏移量
   * @param field将被设置的新值
   * @see putIntVolatile(Object,long,int)
   */
  public native void putOrderedInt(Object obj, long offset, int value);
  /***
   * 参考putOrderedInt()
   */
  public native void putOrderedLong(Object obj, long offset, long value);
  /***
   * 设置obj对象中offset偏移地址对应的object型field的值为指定值。这是一个有序或者
   * 有延迟的putObjectVolatile方法,并且不保证值的改变被其他线程立即看到。
   * 只有在field被volatile修饰并且期望被意外修改的时候使用才有用。
   *
   * @param 包含需要修改field的对象
   * @param obj中long型field的偏移量
   * @param field将被设置的新值
   */
  public native void putOrderedObject(Object obj, long offset, Object value);
  /***
   * 设置obj对象中offset偏移地址对应的整型field的值为指定值,支持volatile store语义
   * 
   * @param 包含需要修改field的对象
   * @param obj中整型field的偏移量
   * @param field将被设置的新值
   */
  public native void putIntVolatile(Object obj, long offset, int value);
  /***
   * 参考putIntVolatile()
   */
  public native void putLongVolatile(Object obj, long offset, long value);
  /***
   * 设置obj对象中offset偏移地址对应的long型field的值为指定值。
   * 
   * @param 包含需要修改field的对象
   * @param obj中long型field的偏移量
   * @param field将被设置的新值
   * @see #putLongVolatile(Object,long,long)
   */
   
  public native void putLong(Object obj, long offset, long value);
  
    /***
   * 获取obj对象中offset偏移地址对应的整型field的值,支持volatile load语义。
   * 
   * @param 包含需要去读取的field的对象
   * @param obj中整型field的偏移量
   */
  public native int getIntVolatile(Object obj, long offset);
  /***
   * 参考getIntVolatile()
   */
   
  public native long getLongVolatile(Object obj, long offset);
  /***
   * 获取obj对象中offset偏移地址对应的long型field的值
   * 
   * @param 包含需要去读取的field的对象
   * @param  obj中long型field的偏移量
   * @see #getLongVolatile(Object,long)
   */
  public native long getLong(Object obj, long offset);
  /***
   * 设置obj对象中offset偏移地址对应的object型field的值为指定值。支持volatile store语义
   * 
   * @param 包含需要修改field的对象
   * @param obj中object型field的偏移量
   * @param field将被设置的新值
   * @see #putObject(Object,long,Object)
   */
  public native void putObjectVolatile(Object obj, long offset, Object value);
  /***
   * 设置obj对象中offset偏移地址对应的object型field的值为指定值。
   * 
   * @param 包含需要修改field的对象
   * @param obj中object型field的偏移量
   * @param field将被设置的新值
   * @see #putObjectVolatile(Object,long,Object)
   */
  public native void putObject(Object obj, long offset, Object value);
  /***
   * 获取obj对象中offset偏移地址对应的object型field的值,支持volatile load语义。
   * 
   * @param 含需要去读取的field的对象
   * @param obj中object型field的偏移量
   */
  public native Object getObjectVolatile(Object obj, long offset);
  /***
   * 获取给定数组中第一个元素的偏移地址。
   * 为了存取数组中的元素,这个偏移地址与arrayIndexScale方法的非0返回值一起被使用。
   * @param 第一个元素地址被获取的class
   * @return 数组第一个元素的偏移地址
   * @see arrayIndexScale(Class)
   */
  public native int arrayBaseOffset(Class arrayClass);
  /***
   * 获取用户给定数组寻址的换算因子,一个合适的换算因子不能返回的时候(例如:基本类型),
   * 返回0,这个返回值能够与arrayBaseOffset一起使用去存取这个数组class中的元素
   * 
   * @param arrayClass the class whose scale factor should be returned.
   * @return the scale factor, or zero if not supported for this array class.
   */
  public native int arrayIndexScale(Class arrayClass);
  
  /***
   * 释放被park创建的在一个线程上的阻塞.这个方法也可以被使用来终止一个先前调用park导致的阻塞.
   * 这个操作操作是不安全的,因此线程必须保证是活的,这是java代码不是native代码。
   * @param 要解除阻塞的线程
   */
  public native void unpark(Thread thread);
  /***
   * 阻塞一个线程直到unpark出现、线程被中断或者timeout时间到期。如果一个unpark调用已经出现了,
   * 这里只计数。timeout为0表示永不过期.当isAbsolute为true时,
   * timeout是相对于新纪元之后的毫秒。否则这个值就是超时前的纳秒数。这个方法执行时
   * 也可能不合理地返回(没有具体原因)
   * 
   * @param 如果为true timeout的值是一个相对于新纪元之后的毫秒数
   * @param 可以是一个要等待的纳秒数,或者是一个相对于新纪元之后的毫秒数直到到达这个时间点
   */
  public native void park(boolean isAbsolute, long time);
}



版权声明:本文为weixin_45583303原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。