Unsafe类是在sun.misc包中,使Java拥有了类似C语言指针操作内存的功能。该类使用了单例模式,需要通过一个静态方法getUnsafe()来获取实例,该方法会抛出SecurityException异常,其源码如下:

@CallerSensitive
public static Unsafe getUnsafe() {
    Class var0 = Reflection.getCallerClass();
    if(!VM.isSystemDomainLoader(var0.getClassLoader())) 
    {
        throw new SecurityException("Unsafe");
    } else {
        return theUnsafe;
    }
}

也可以使用Java反射技术来获取实例,方法如下:

Filed field = Unsafe.class.getDeclaredFiled("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe)field.get(null);

Unsafe类提供了以下这些功能:

  • 操作内存

该部分包括allocateMemory(分配内存)、reallocateMemory(重新分配内存)、setMemory(设置内存)、copyMemory(拷贝内存)、freeMemory(释放内存)等方法。

allocateMemory(long size);

该方法分配一块给定大小的内存空间,这块内存可能包含有垃圾数据(没有初始化清零)。如果分配失败则抛出java.lang.OutOfMemoryError。该方法返回一个非0的内存地址。

reallocateMemory(long address, long size);

该方法重新分配一块给定大小的内存空间,将数据从address指定的内存缓冲区拷贝到新分配的内存区。如果address为0则和allocateMemory方法相同。

freeMemory(long address);

该方法释放以上两个方法分配的内存空间。如果address为0则什么也不做。

  • 操作数组
arrayBaseOffset(Class arrayClass);

该方法获取给定数组中第一个元素的内存地址偏移量。

arrayIndexScale(Class arrayClass);

该方法获取给定数组中每个元素内存地址的比例因子,获取不到则返回0。

这两个方法配合使用可以获取数组中每个元素在内存中的位置。

  • 实例化构造函数私有的类
allocateInstance(Class class);

该方法在实例化对象的时候,能够避免调用构造函数,常用于反序列化过程中,既保证安全又可以节省时间。

Student student = (Student)unsafe.allocateInstance(Student.class);
student.setName("鸿雁出塞北,鹧鸪生江南");
student.setAge(18);
System.out.println(student);
//输出结果:
//Student [name=鸿雁出塞北,鹧鸪生江南, age=18]

public class Student {
    private String name;//姓名
    private int age;//年龄

    private Student() {}//私有构造函数
    //省略getters & setters
    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }
}
  • 获取字段对应的内存地址偏移量,并通过内存地址偏移量修改字段的值
for(Field f : Student.class.getDeclaredFields()) {
    System.out.println(f.getName() + "字段对应的内存地址偏移 : " + unsafe.objectFieldOffset(f));
}
//输出结果:
//name字段对应的内存地址偏移 : 16
//age字段对应的内存地址偏移 : 12

getInt(Object obj, long offset);
getIntVolatile(Object obj, long offset);
putInt(Object obj, long offset, int value);
putOrderedInt(Object obj, long offset, int value);
putIntVolatile(Object obj, long offset, int value);
  • CAS操作
compareAndSwapObject(Object obj, long offset, Object expect, Object update);
compareAndSwapInt(Object obj, long offset, int expect, int update);
compareAndSwapLong(Object obj, long offset, long expect, long update);
  • 阻塞和唤醒线程
park(boolean isAbsolute, long time);

该方法将一个线程进行挂起,该线程将一直阻塞直到unpark()方法被调用、线程被中断或者time超时等条件出现。isAbsolute如果为true则表示采用时间戳,否则表示采用相对时间。

unpark(Object thread);

该方法可以终止线程的挂起状态,使其恢复正常。

整个并发框架中对线程的挂起操作被封装在LockSupport类中,LockSupport类中有各种重载的pack()方法,但最终都调用了Unsafe.park()方法。

public class ParkThread extends Thread {
    @Override
    public void run() {
        System.out.println("江汉曾为客,相逢每醉还");
        LockSupport.parkNanos(60*1000*1000*1000);//阻塞60s
        System.out.println("何因不归去,淮上有秋山");
    }
}

public class UnparkThread extends Thread {
    private Thread thread;
    public UnparkThread(Thread thread) {
        this.thread = thread;
    }
    @Override
    public void run() {
        System.out.println("浮云一别后,流水十年间");
        LockSupport.unpark(thread);//唤醒被阻塞的线程
    }
}

public static void main(String[] args) {
    Thread parkThread = new ParkThread();
    Thread unparkThread = new UnparkThread(parkThread);
    parkThread.start();
    unparkThread.start();
    try {
        unparkThread.join();
    } catch(InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("欢笑情如旧,萧疏鬓已斑");
}
posted on 2017-12-06 17:08  SmileorSilence  阅读(483)  评论(0)    收藏  举报