疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java中的指针:Unsafe类


 

   

  如果你对技术有着不折不挠的追求,应该还会特别在意incrementAndGet() 方法中compareAndSet()的实现。现在,就让我们更进一步看一下它把!

  public final boolean compareAndSet(int expect, int update){

  returnunsafe.compareAndSwapInt(this, valueOffset, expect, update);

  }

  在这里,我们看到一个特殊的变量unsafe。它是sun.misc.Unsafe类型。从名字看,这个类应该是封装了一些不安全的操作。那什么操作是不安全的呢?学习过C或者C++的话,大家应该知道,指针是不安全的。这也是在Java中把指针去除的重要原因。如果指针指错了位置,或者计算指针偏移量时出错,结果可能是灾难性的,你很有可能会覆盖别人的内存,导致系统奔溃。

  而这里的Unsafe就是封装了一些类似指针的操作。compareAndSwapInt()方法是一个navtive方法。它的几个参数含义如下:

  public final native boolean compareAndSwapInt(Object o,long offset,int expected,int x);

  第一个参数o为给定的对象,offset为对象内的偏移量(其实就是一个字段到对象头部的偏移量,通过这个偏移量可以快速定位字段),expected表示期望值,x表示要设置的值。如果指定的字段的值等于expected,那么就会把它设置为x。

  不难看出,compareAndSwapInt()方法的内部,必然是使用CAS原子指令来完成的。此外,Unsafe类还提供了一些方法,主要有以下几个(以Int操作为例,其他数据类型是类似的):

  复制代码

  //获得给定对象偏移量上的int值

  public native int getInt(Object o, long offset);

  //设置给定对象偏移量上的int值

  public native void putInt(Object o, long offset, int x);

  //获得字段在对象中的偏移量

  public native long objectFieldOffset(Field f);

  //设置给定对象的int值,使用volatile语义

  public native void putIntVolatile(Object o, long offset,int x);

  //获得给定对象对象的int值,使用volatile语义

  public native int getIntVolatile(Object o, long offset);

  //和putIntVolatile()一样,但是它要求被操作字段就是volatile类型的

  public native void putOrderedInt(Object o, long offset, intx);

  复制代码

  如果大家还记得“3.3.4 深度剖析ConcurrentLinkedQueue”一节中的描述的ConcurrentLinkedQueue实现,应该对ConcurrentLinkedQueue中的Node还有些印象。Node一些CAS操作也都是使用Unsafe类来实现的。大家可以回顾一下,以加深对Unsafe类的印象。

  这里就可以看到,虽然Java抛弃了指针。但是在关键时刻,类似指针的技术还是必不可少的。这里底层的Unsafe实现就是最好的例子。但是很不幸,JDK的开发人员并不希望大家使用这个类。获得Unsafe实例的方法是调动其工厂方法getUnsafe()。但是,它的实现却是这样:

  复制代码

  public static Unsafe getUnsafe() {

  Class cc =Reflection.getCallerClass();

  if(cc.getClassLoader() != null)

  throw newSecurityException("Unsafe");

  return theUnsafe;

  }

  复制代码

  注意加粗部分的代码,它会检查调用getUnsafe()函数的类,如果这个类的ClassLoader不为null,就直接抛出异常,拒绝工作。因此,这也使得我们自己的应用程序无法直接使用Unsafe类。它是一个JDK内部使用的专属类。

  注意:根据Java 类加载器的工作原理,应用程序的类由AppLoader加载。而系统核心类,如rt.jar中的类由Bootstrap类加载器加载。Bootstrap加载器没有Java对象的对象,因此试图获得这个类加载器会返回null。所以,当一个类的类加载器为null时,说明它是由Bootstrap加载的,而这个类也极有可能是rt.jar中的类。