前言

Unsafe,顾名思义,一个不安全的类,那么jdk的开发者为什么要设计一个不安全的类呢?这个类为什么会不安全呢?现在就让我们来揭开Unsafe类的神秘面纱。

1.概述

作为java开发者的我们都知道,java是没有指针的,默认是由JVM进行内存的分配与垃圾回收,那就意味着java不能直接操作内存了?其实不是的,Unsafe类通过JNI的方式访问本地的C++实现库从而使java具有了直接操作内存空间的能力,但这同时也带来了一定的问题,如果不合理地使用Unsafe类操作内存空间,可能导致内存泄漏,是很危险的,这就要看开发者对Unsafe类的熟悉程度了,不是非常熟悉这个类,不推荐使用。而且java官方也是不推荐开发者使用的,官方关于Unsafa的文档很少,并且这个类在jdk8中也不开源,在Openjdk中也是不开源的,只能通过IDE反编译来看它的源码,它的源码中好多的方法都标注了@Deprecated注解,而且也没有注释(也许是反编译过来看不到注释)。(之前传说jdk9要这个类删除,现在jdk12都出来了,这个类不但没有删除,还在jdk.internal.misc包下增加了一个Unsafe类。并且自从jdk9开始,Unsafe类也开源了,与sun.misc.Unsafe不同的是,jdk.internal.misc.Unsafe是不开放给开发者的,应该是供jdk的开发和维护人员使用的)。既然官方不推荐使用,那为什么还要设计这个类呢?因为Unsafe提供了硬件级别的原子性操作,JUC包中用到的CAS算法就是Unsafe类提供的,jdk的开发者用这个类实现了JUC包中的一部分核心功能。同时,Unsafe类就像是java给开发者提供的一个后门,让开发者使用Unsafe就可以在任意内存地址读写数据,而不用经过JVM的调度,也使java更加地灵活,具有一定的C和C++访问内存的能力,对于系统调优有一定的帮助,对于优秀的开发者,也可以使用这个类造一些实用的轮子。可见,Unsafe类还是有一定的用处的,不过在使用时需要小心谨慎。

2.获取Unsafe

2.1Unsafe类的构造器是私有的,采用了单例模式,提供了一个静态方法,是不是可以通过这个静态方法获取到Unsafe对象呢?

public class UnsafeTest {public static void main(String[] args) {Unsafe unsafe = Unsafe.getUnsafe();System.out.println(unsafe);}
}

报错了,Unsafe本身提供了获取实例的静态方法,那为什么我们在使用时会报错呢?

看下源码(jdk11的,jdk8不开源,反编译过来的不太好看):

private Unsafe() {}private static final Unsafe theUnsafe = new Unsafe();@CallerSensitive
public static Unsafe getUnsafe() {Class<?> caller = Reflection.getCallerClass();if (!VM.isSystemDomainLoader(caller.getClassLoader()))throw new SecurityException("Unsafe");return theUnsafe;
}

看到源码,是不是上面的异常有眉目了?就是throw new SecurityException(“Unsafe”);这句抛出的异常,我们看为什么抛出异常:

if (!VM.isSystemDomainLoader(caller.getClassLoader()))throw new SecurityException("Unsafe");

抛出异常就说明不满足if条件,isSystemDomainLoader(caller.getClassLoader())方法就是检查调用者的类加载器是否是启动类加载器,抛出异常就是因为我们自己创建的类不是由启动类加载器加载,而是默认由系统类加载器加载的,故抛出异常。

2.2使用反射获取Unsafe实例

public class UnsafeTest {public static void main(String[] args) {final Field theUnsafe;try {theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);Unsafe unsafe  = (Unsafe) theUnsafe.get(null);System.out.println(unsafe.getClass().getClassLoader());System.out.println(unsafe);} catch (NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}}
}

发现使用反射获取Unsafe实例是可以获取到的,打印的类加载器的信息为null,说明是由启动类加载加载的。

2.3jdk11的jdk.internal.misc包中的Unsafe类的getUnsafe静态方法中没有要求调用者必须要由启动类加载器加载,源码如下:

private Unsafe() {}private static final Unsafe theUnsafe = new Unsafe();public static Unsafe getUnsafe() {return theUnsafe;
}

这回是不是可以直接获取了呢?试一下:

public class UnsafeTest {public static void main(String[] args) {jdk.internal.misc.Unsafe unsafe = Unsafe.getUnsafe();System.out.println(unsafe);}
}

又报错了,异常信息如下:

Exception in thread "main" java.lang.IllegalAccessError: class hello.UnsafeTest (in unnamed module @0x6e8dacdf) cannot access class jdk.internal.misc.Unsafe (in module java.base) because module java.base does not export jdk.internal.misc to unnamed module @0x6e8dacdfat hello.UnsafeTest.main(UnsafeTest.java:9)

意思就是这个类没有开放给我们,我们不能访问。

看一下java.base下的module-info.java文件,可以看到这个Unsafe类确实没有开放给我们,只是开放给以下的jdk的部分模块:

exports jdk.internal.misc tojava.desktop,java.logging,java.management,java.naming,java.net.http,java.rmi,java.security.jgss,java.sql,java.xml,jdk.attach,jdk.charsets,jdk.compiler,jdk.internal.vm.ci,jdk.jfr,jdk.jlink,jdk.jshell,jdk.net,jdk.scripting.nashorn,jdk.scripting.nashorn.shell,jdk.unsupported;

看来直接获取是获取不到的,除非修改openjdk11源码中的module-info.java文件,将jdk.internal.misc这个包开放出来,重新编译。不过作为普通开发者是没有必要修改源码的,通过反射获取就可以了。

jdk11开放给开发者的也是sun.misc包中的Unsafe类,这个包在jdk.unsupported模块下。也是需要使用反射获取实例的。

注:以下涉及到的源码都为java11的源码。

3.重要API分析

Student类:

public class Student {private String name;private int age;public static String info;static {info = "这是一个优秀的学生";}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Student(String name, int age) {this.name = name;this.age = age;}public Student() {}
}

开放给我们能用的位于sun.misc包下的Unsafe类的实现其实是调用了更加底层的位于jdk.internal.misc包下的Unsafe类,native方法就是位于这个类中,这点与java8的Unsafe类不同,java8只有一个Unsafe类,这个类中大多都为native方法:

sun.misc.Unsafe源码:

private static final Unsafe theUnsafe = new Unsafe();
private static final jdk.internal.misc.Unsafe theInternalUnsafe = jdk.internal.misc.Unsafe.getUnsafe();

3.1获取对象字段或静态属性的内存地址偏移量

源码如下:

//获取对象属性的便宜量
@ForceInline
public long objectFieldOffset(Field f) {return theInternalUnsafe.objectFieldOffset(f);
}
//获取静态属性的偏移量
@ForceInline
public long staticFieldOffset(Field f) {return theInternalUnsafe.staticFieldOffset(f);
}

举个栗子:

@Test
public void testGetFieldOffset() throws NoSuchFieldException, IllegalAccessException {final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);Unsafe unsafe = (Unsafe) theUnsafe.get(null);//获取字段偏移量long address = unsafe.objectFieldOffset(Student.class.getDeclaredField("name"));System.out.println(address);//获取静态字段偏移量long staticAddress = unsafe.staticFieldOffset(Student.class.getDeclaredField("info"));System.out.println(staticAddress);
}

3.2内存操作,包括内存的分配,释放,复制

3.2.1分配内存:

//参数为需要分配的内存的字节数
public long allocateMemory(long bytes) {return theInternalUnsafe.allocateMemory(bytes);
}

3.2.2重新分配内存(内存扩展):

//第一个参数为已经分配的内存,第二个参数为要扩展的字节数
public long reallocateMemory(long address, long bytes) {return theInternalUnsafe.reallocateMemory(address, bytes);
}

3.2.3内存释放:

public void freeMemory(long address) {theInternalUnsafe.freeMemory(address);
}

举个栗子:

@Test
public void testMemoryOperate() throws NoSuchFieldException, IllegalAccessException {final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);Unsafe unsafe = (Unsafe) theUnsafe.get(null);//分配内存long address = unsafe.allocateMemory(4);System.out.println(address);//扩展内存long newAddress = unsafe.reallocateMemory(address, 8);System.out.println(newAddress);//释放内存unsafe.freeMemory(newAddress);
}

关于内存操作还有几个方法,这里列出,不做举例,这几个方法笔者平时不常用,源码中也不常用。

3.2.4设置内存:将给定的内存设置为固定值

public void setMemory(Object o, long offset, long bytes, byte value) {theInternalUnsafe.setMemory(o, offset, bytes, value);
}public void setMemory(long address, long bytes, byte value) {theInternalUnsafe.setMemory(address, bytes, value);
}

3.2.5内存复制:

public void copyMemory(Object srcBase, long srcOffset,Object destBase, long destOffset,long bytes) {theInternalUnsafe.copyMemory(srcBase, srcOffset, destBase, destOffset, bytes);
}public void copyMemory(long srcAddress, long destAddress, long bytes) {theInternalUnsafe.copyMemory(srcAddress, destAddress, bytes);
}

3.3内存屏障
为了保证内存的可见性,java编译器在生成指令序列的适当位置会插入内存屏障指令类禁止特定类型的处理器重排序,java内存模型(JMM)把内存屏障指令分为4类:

  • LoadLoad(Load1,LoadLoad,Load2):确保load1数据的装载先于load2及所有后序装载指令的装载。
  • LoadStore(Load1,LoadStore,Store2):确保Load1数据的装载先于Store2及所有后序存储指令刷新内存。
  • StoreStore(Store1,StoreStore,Store2):确保Store1数据刷新内存先于Store2及所有后序存储指令刷新内存。
  • StoreLoad(Store1,StoreLoad,Load2):确保Store1数据刷新内存先于Load2及所有后序装载指令的装载。该屏蔽指令会使该屏蔽之前的所有内存访问指令执行完成后才执行屏蔽之后的内存访问指令。并且这个指令是一个全能的指令,同时具备以上三个内存屏蔽指令的功能。
//确保在屏蔽之前加载(读操作),屏蔽之后的读写操作不会重排序,相当于LoadLoad 加上 LoadStore 屏障
public void loadFence() {theInternalUnsafe.loadFence();
}
//确保在屏蔽之前加载和存储(读写操作),屏蔽之后的写操作不会重排序,相当于StoreStore 加上 LoadStore 屏障
public void storeFence() {theInternalUnsafe.storeFence();
}
//确保在屏蔽之前读写操作,屏蔽之后的读写操作不会重排序,同时具有以上两个方法的功能,还具有StoreLoad屏蔽的功能
public void fullFence() {theInternalUnsafe.fullFence();
}

3.4通过操作内存从一个给定的java变量获取或设置值

这类方法有针对于基本数据类型和对象类型(下面列出比分源码):

//从基本数据类型变量获取值
public int getInt(Object o, long offset) {return theInternalUnsafe.getInt(o, offset);
}
//设置基本类型变量的值
public void putInt(Object o, long offset, int x) {theInternalUnsafe.putInt(o, offset, x);
}
//从对象类型变量获取值
public Object getObject(Object o, long offset) {return theInternalUnsafe.getObject(o, offset);
}
//设置对象类型变量的值
public void putObject(Object o, long offset, Object x) {theInternalUnsafe.putObject(o, offset, x);
}

栗子(以int类型为例):

@Test
public void testGetAndPutInt() throws NoSuchFieldException, IllegalAccessException {final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);Unsafe unsafe = (Unsafe) theUnsafe.get(null);Student student = new Student("liming",16);//通过objectFieldOffset方法获取学生年龄在内存中的偏移量long offset = unsafe.objectFieldOffset(Student.class.getDeclaredField("age"));System.out.println(offset);//通过操作内存的方式获取学生年龄int anInt = unsafe.getInt(student, offset);System.out.println(anInt);System.out.println(student.getAge());//通过操作内存的方式修改学生年龄unsafe.putInt(student,offset,30);System.out.println(student.getAge());
}

栗子(以Object类型为例):

@Test
public void testGetAndPutObject() throws NoSuchFieldException, IllegalAccessException {final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);Unsafe unsafe = (Unsafe) theUnsafe.get(null);Student student = new Student("liming",16);//通过objectFieldOffset方法获取学生姓名在内存中的偏移量long offset = unsafe.objectFieldOffset(Student.class.getDeclaredField("name"));//获取对象属性System.out.println(unsafe.getObject(student,offset));System.out.println(student.getName());//修改对象属性unsafe.putObject(student,offset,"xiahua");System.out.println(student.getName());
}

3.5通过操作内存从一个内存地址获取或设置值

这类方法和3.4一样,有基本数据类型和对象类型,这里以int类型为例举个栗子:

//从一个内存地址获取整型值
public int getInt(long address) {return theInternalUnsafe.getInt(address);
}
//给一个内存地址设置一个整型值
public void putInt(long address, int x) {theInternalUnsafe.putInt(address, x);
}

栗子:

@Test
public void testGetAndPutInt2() throws NoSuchFieldException, IllegalAccessException {final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);Unsafe unsafe = (Unsafe) theUnsafe.get(null);//通过allocateMemory方法分配一个8具有8个字节的内存空间long address = unsafe.allocateMemory(8);//给指定的内存空间写入一个整型值unsafe.putInt(address,60);//获取内存空间的值System.out.println(unsafe.getInt(address));//重新分配内存,扩大到16字节final long newAddress = unsafe.reallocateMemory(address, 16);unsafe.putInt(newAddress,100);System.out.println(unsafe.getInt(newAddress));//回收分配的内存空间unsafe.freeMemory(newAddress);
}

3.6通过操作内存从一个给定的变量获取或设置一个具有volatile语义的值

这类方法也和3.4一样,有基本数据类型和对象类型,其实就是volatile版的3.4方法,这里以int类型为例:

//从一个变量获取具有volatile语义的整型值
public int getIntVolatile(Object o, long offset) {return theInternalUnsafe.getIntVolatile(o, offset);
}
//给一个变量设置具有volatile语义的值
public void putIntVolatile(Object o, long offset, int x) {theInternalUnsafe.putIntVolatile(o, offset, x);
}

栗子:

@Test
public void testGetAndPutIntVolatile() throws NoSuchFieldException, IllegalAccessException {final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);Unsafe unsafe = (Unsafe) theUnsafe.get(null);long offset = unsafe.objectFieldOffset(Student.class.getDeclaredField("age"));//设置学生的年龄,使用volatile语义,设置后立马更新到内存对其他线程可见。unsafe.putIntVolatile(Student.class,offset,32);System.out.println(unsafe.getIntVolatile(Student.class,offset));
}

3.7通过操作内存从一个给定的变量惰性地设置一个具有volatile语义的值

这类方有三个,有基本数据类型和对象类型,其实就是lazy加volatile版的3.4方法,这里以int类型为例:

public void putOrderedObject(Object o, long offset, Object x) {theInternalUnsafe.putObjectRelease(o, offset, x);
}
public void putOrderedInt(Object o, long offset, int x) {theInternalUnsafe.putIntRelease(o, offset, x);
}
public void putOrderedLong(Object o, long offset, long x) {theInternalUnsafe.putLongRelease(o, offset, x);
}

栗子:

/*
*PutIntVolatile的lazy版,惰性设值,设值后内存不能保证立即对其他线程可见,并且变量和后序内存可以重排序
*/@Testpublic void testPutIntVolatileLazy() throws NoSuchFieldException, IllegalAccessException {final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);Unsafe unsafe = (Unsafe) theUnsafe.get(null);long offset = unsafe.objectFieldOffset(Student.class.getDeclaredField("age"));unsafe.putOrderedInt(Student.class,offset,30);System.out.println(unsafe.getIntVolatile(Student.class,offset));}

3.8通过操作内存获取或设置数组元素值

//获取数组中第一个元素在内存的偏移地址
public int arrayBaseOffset(Class<?> arrayClass) {return theInternalUnsafe.arrayBaseOffset(arrayClass);
}
//获取数组增量元素在内存中的地址
public int arrayIndexScale(Class<?> arrayClass) {return theInternalUnsafe.arrayIndexScale(arrayClass);
}

栗子:

@Test
public void testArrayOperate() throws NoSuchFieldException, IllegalAccessException {final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);Unsafe unsafe = (Unsafe) theUnsafe.get(null);int[] array = new int[]{1,5,8,9,6,4,7};//获取数组中第一个元素在内存的偏移地址int offset = unsafe.arrayBaseOffset(array.getClass());//获取数组增量元素在内存中的地址int indexScale = unsafe.arrayIndexScale(array.getClass());//通过操作内存地址获取数组的第一个元素System.out.println(unsafe.getInt(array,offset));//通过操作内存修改数组的第一个元素unsafe.putInt(array,offset,3);System.out.println(unsafe.getInt(array,offset));//通过操作内存获取数组的第三个元素,arrayBaseOffset和arrayIndexScale共同使用,就可以获得任意一个数组元素在内存的地址System.out.println(unsafe.getInt(array,indexScale*2+offset));//通过循环遍历数组元素for (int i = 0; i < array.length; i++) {System.out.print(unsafe.getInt(array,i*indexScale+offset) + " ");}
}

3.9从一个给定的内存地址获取或设置一个本地指针

//从给定的内存地址获取一个本地指针
public long getAddress(long address) {return theInternalUnsafe.getAddress(address);
}
//从给定的内存地址设置一个本地指针
public void putAddress(long address, long x) {theInternalUnsafe.putAddress(address, x);
}

栗子:

@Test
public void testGetAndPutAddress() throws NoSuchFieldException, IllegalAccessException {final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);Unsafe unsafe = (Unsafe) theUnsafe.get(null);//通过allocateMemory方法分配一个8具有8个字节的内存空间long address = unsafe.allocateMemory(8);//给这个内存中存放一个本地指针unsafe.putAddress(address,200);//获取指定内存地址的指针System.out.println(unsafe.getAddress(address));//打印通过putAddress存放的本地指针的大小System.out.println(unsafe.addressSize());//打印内存页面的大小,通常为4k,即4096   //关于内存页面不了解的可以看下操作系统相关的知识System.out.println(unsafe.pageSize());//释放内存unsafe.freeMemory(address);
}

3.10硬件级别的CAS操作

关于CAS算法不了解的小伙伴可以参考我的另一篇文章:CAS算法

//CAS操作对象类型
public final boolean compareAndSwapObject(Object o //对象, long offset //内存偏移量,Object expected, //预期值Object x) { //新值return theInternalUnsafe.compareAndSetObject(o, offset, expected, x);
}
//CAS操作int类型
public final boolean compareAndSwapInt(Object o, long offset,int expected,int x) {return theInternalUnsafe.compareAndSetInt(o, offset, expected, x);
}
//CAS操作long型
public final boolean compareAndSwapLong(Object o, long offset,long expected,long x) {return theInternalUnsafe.compareAndSetLong(o, offset, expected, x);
}

关于CAS操作,Unsafe源码只给出以上三个方法,其他类型的CAS操作可以转化为以上三种类型,比如boolean类型的CAS操作可以转化为int型的CAS操作。

栗子:

@Test
public void testCAS() throws NoSuchFieldException, IllegalAccessException {final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);Unsafe unsafe = (Unsafe) theUnsafe.get(null);long offset = unsafe.objectFieldOffset(Student.class.getDeclaredField("age"));unsafe.putInt(Student.class,offset,25);System.out.println(unsafe.getInt(Student.class,offset));//通过Unsafe提供的硬件级别的原子操作 CAS算法更新学生的年龄unsafe.compareAndSwapInt(Student.class,offset,24,35);System.out.println(unsafe.getInt(Student.class,offset));unsafe.compareAndSwapInt(Student.class,offset,25,35);System.out.println(unsafe.getInt(Student.class,offset));
}

3.11线程的挂起与恢复

Unsafe类提供了线程的挂起与恢复方法,JUC中的锁会用到Unsafe提供的线程挂起与恢复方法,源码如下:

//线程挂起,直到unpark调用或者打断或者超时,线程恢复。
//isAbsolute设置是否为绝对时间,如果为true,则超时时间的单位为毫秒
//isAbsolute为false,time设置为0,就是一直阻塞,如果time不为0,则超时时间的单位为纳秒
public void park(boolean isAbsolute, long time) {theInternalUnsafe.park(isAbsolute, time);
}
//线程恢复
public void unpark(Object thread) {theInternalUnsafe.unpark(thread);
}

栗子:

@Test
public void testParkAndUnPark() throws NoSuchFieldException, IllegalAccessException {final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);Unsafe unsafe = (Unsafe) theUnsafe.get(null);//写一个线程执行一个任务Thread thread = new Thread(() -> System.out.println("hello"));thread.start();unsafe.unpark(Thread.currentThread());  //如果不加这句,线程或一直处于阻塞状态unsafe.park(false,0);
}

3.12从一个变量获取值并让它自动增加给定的值

public final int getAndAddInt(Object o, long offset, int delta) {return theInternalUnsafe.getAndAddInt(o, offset, delta);
}public final long getAndAddLong(Object o, long offset, long delta) {return theInternalUnsafe.getAndAddLong(o, offset, delta);
}

举个栗子:

@Test
public void testGetAndAddInt() throws NoSuchFieldException, IllegalAccessException {final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);Unsafe unsafe = (Unsafe) theUnsafe.get(null);long offset = unsafe.objectFieldOffset(Student.class.getDeclaredField("age"));unsafe.putInt(Student.class,offset,30);System.out.println(unsafe.getAndAddInt(Student.class,offset,3));System.out.println(unsafe.getInt(Student.class,offset));
}

3.13从一个变量获取值并设置成另一个值

public final int getAndSetInt(Object o, long offset, int newValue) {return theInternalUnsafe.getAndSetInt(o, offset, newValue);
}public final long getAndSetLong(Object o, long offset, long newValue) {return theInternalUnsafe.getAndSetLong(o, offset, newValue);
}public final Object getAndSetObject(Object o, long offset, Object newValue) {return theInternalUnsafe.getAndSetObject(o, offset, newValue);
}

栗子:

public void testGetAndSetInt() throws NoSuchFieldException, IllegalAccessException {final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);Unsafe unsafe = (Unsafe) theUnsafe.get(null);long offset = unsafe.objectFieldOffset(Student.class.getDeclaredField("age"));unsafe.putInt(Student.class,offset,30);System.out.println(unsafe.getAndSetInt(Student.class,offset,32));System.out.println(unsafe.getInt(Student.class,offset));
}

4.Unsafe底层实现

既然Unsafe能够操作内存,那么它的底层一定需要JVM的支持,在OpenJDK的HotSpot虚拟机源码中有Unsafe类的JVM层面的实现,对应于prims目录中的Unsafe.cpp,java11的jvm源码关于Unsafe的实现还多了Unsafe.hpp这个头文件。Unsafe.cpp这个文件小两千行代码,且注释比较少,要想全搞明白需要Debug很久,这里我就不具体分析了,以后写JVM源码时再具体分析这个类。如果感兴趣的朋友可以下载OpenJDK源码,编译后搭建调试环境,关于JVM源码编译,大家可以参考我的这篇文章:Ubuntu下编译openjdk8

Ubuntu下编译openjdk11

调试环境的搭建,大家可以参考我的这篇文章:JVM源码调试环境搭建

OpenJDK大家可以到官网下载,也可以到我的github下载:

  • openjdk8
  • openjdk11

Unsafe类源码解析相关推荐

  1. java.lang 源码剖析_java.lang.Void类源码解析

    在一次源码查看ThreadGroup的时候,看到一段代码,为以下: /* * @throws NullPointerException if the parent argument is {@code ...

  2. Node 学习六、核心模块 events之 01 events 和 EventEmitter 类、发布订阅、EventEmitter 类源码解析和模拟实现

    events 事件模块 events 与 EventEmitter node.js 是基于事件驱动的异步操作架构,内置 events 模块 events 模块提供了 EventEmitter 类 这个 ...

  3. Scroller类源码解析及其应用(一)

    滑动是我们在自定义控件时候经常遇见的难题,让新手们倍感困惑,这篇文章主要介绍Scroller类的源码,告诉打击这个到底有什么用,怎么使用它来控制滑动.另外,我还会结合一个简单的例子,来看一下这个类的应 ...

  4. Java String类源码解析

    String直接继承Object 含有一个char[] value,还有一个int hash默认值为0 new String()的构造产生的是一个值为""的字符数组 String( ...

  5. Java FileReader InputStreamReader类源码解析

    FileReader 前面介绍FileInputStream的时候提到过,它是从文件读取字节,如果要从文件读取字符的话可以使用FileReader.FileReader是可以便利读取字符文件的类,构造 ...

  6. Java集合---Arrays类源码解析

    一.Arrays.sort()数组排序 Java Arrays中提供了对所有类型的排序.其中主要分为Primitive(8种基本类型)和Object两大类. 基本类型:采用调优的快速排序: 对象类型: ...

  7. 【多线程】ThreadPoolExecutor类源码解析----续(二进制相关运算)

    前言 在之前阅读 ThreadPoolExecutor 源码的时候,发现代码里用到了一些二进制相关的位运算之类的代码,看起来有些费劲了,所以现在大概总结了一些笔记,二进制这东西吧,不难,就跟数学一样, ...

  8. ReentrantLock类源码解析

    先看ReentrantLock类的构造方法和成员 private final Sync sync;public ReentrantLock() {sync = new NonfairSync(); } ...

  9. Java1.7ConcurrentHashMap类源码解析

    先来看ConcurrentHashMap类的成员. static final int DEFAULT_INITIAL_CAPACITY = 16;static final float DEFAULT_ ...

最新文章

  1. [转] Java中public,private,final,static等概念的解读
  2. 【Verilog HDL 训练】第 11 天(分频电路)
  3. python详细安装教程3.7.0-Linux 安装Python3.7.0
  4. SpringBoot @PostConstruct和@PreDestroy使用详解
  5. (进阶篇)Redis6.2.0 集群 哨兵模式_哨兵工作原理_02
  6. Android -- 自定义ScrollView实现放大回弹效果
  7. c++_导入/导出excel文件
  8. 网友反映摩拜单车无法扫码 回应:系技术问题 现已恢复服务
  9. c语言2阶数组指针,C语言的指针(进阶篇章之二)
  10. 网络配置_CentOS8 网络配置
  11. Visual Studio最常用、最高效的快捷键
  12. ETCD for java_etcd-java使用
  13. 阿里云服务器安全组配置-有关访问实例异常的解决办法
  14. 网络编程练手小项目---英英词典
  15. 爬取豆瓣排名前100的电影
  16. Windows 10的最新版本是什么?
  17. Bing(必应)搜索,为什么用户越来越多?
  18. Hadoop3.x集群搭建及配置的完整操作流程
  19. JavaScript的学习8——underscore
  20. 电脑应用图标变成白纸

热门文章

  1. 微信支付 postman_微信版花呗“分付”开通入口在哪?2020年微信分付开通最全攻略!...
  2. 如何在键盘上输出摄氏度“°C”
  3. matlab pq变换,PQ变换与DQ变换的理解与推导
  4. 维修iPhone的home键时,有的店说指纹键和返回键只能选一个是什么意思?
  5. SOR术语中英文对照翻译、简称说明、基础知识总汇
  6. 使用国内镜像网站解决github clone速度慢问题
  7. 肖臻公开课(七)——比特币中的挖矿难度
  8. 《唐门逆天录》隐私政策
  9. python核心编程第3版第1章 正则表达式【读书笔记】
  10. python期货数据 库_GitHub - kozyan/tqsdk-python: 天勤量化开发包, 期货量化, 实时行情/历史数据/实盘交易...