精华内容
下载资源
问答
  • Unsafe类

    2020-07-25 13:33:23
    Unsafe类的介绍 Java中基于操作系统级别的原子操作类sun.misc.Unsafe,它是Java中对大多数锁机制实现的最基础类。请注意,JDK 1.8和之前JDK版本的中sun.misc.Unsafe类可提供的方法有较大的变化,本文基于JDK 1.8。...

    Unsafe类的介绍

    Java中基于操作系统级别的原子操作类sun.misc.Unsafe,它是Java中对大多数锁机制实现的最基础类。请注意,JDK 1.8和之前JDK版本的中sun.misc.Unsafe类可提供的方法有较大的变化,本文基于JDK 1.8。sun.misc.Unsafe类提供的原子操作基于操作系统直接对CPU进行操作,而以下这些方法又是sun.misc.Unsafe类中经常被使用的:

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

    1、获取Unsafe对象实例

    Unsafe类是一个单例,调用的方法为 getUnsafe(),在获取实例的函数中有一步判断,判断检查该CallerClass是不是由系统类加载器BootstrapClassLoader加载,我们知道bootstrapClass是最高层的加载器,底层由C++实现,没有父类加载器,所有由该系统类加载器加载的类调用getClassLoader()会返回null,所以要检查类是否为bootstrap加载器加载只需要检查该方法是不是返回null。

    复制代码

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

    复制代码

    上边获得 theUnsafe 对象是java内部使用的,因为 JDK源码中对这个类进行了严格限制,我们不能通过常规new的方式去获取该类的实例,也不能通过Unsafe.getUnsafe 获得Unsafe对象实例;

    那么我们通过什么方式获得该对象实例,这里就用到 强大的反射机制 自带暴力访问buff :

    在Unsafe类中有一个私有成员变量theUnsafe,因此我们可以通过反射将private单例实例的accessible设置为true,然后通过Field的get方法获取,如下。

    Field f = Unsafe.class.getDeclaredField("theUnsafe"); //获得名为 theUnsafe 的属性 即使为私有 同样获得
    f.setAccessible(true);
    Unsafe unsafe = (Unsafe) f.get(null);  //如果为静态属性 则get 参数为null

     

    2、通过Unsafe类可以分配内存,可以释放内存;

    类中提供的3个本地方法allocateMemory、reallocateMemory、freeMemory分别用于分配内存,扩充内存和释放内存,与C语言中的3个方法对应: malloc(), realloc(), free() [直接开辟内存,释放内存,操作处理使用指针];这里不过多介绍;

    public native long allocateMemory(long l);
    public native long reallocateMemory(long l, long l1);
    public native void freeMemory(long l);

     

    3、可以定位对象某字段的内存位置,也可以修改对象的字段值,即使它是私有的;

      字段的定位:

      对象的字段在主存中的偏移位置:ObjectFieldOffSet    staticFieldOffset    

    JAVA中对象的字段的定位可能通过staticFieldOffset方法实现,该方法返回给定field的内存地址偏移量,这个值对于给定的filed是唯一的且是固定不变的。

      对象的整型或者双精度浮点型在主存中的偏移位置:getIntVolatile, getLong

    getIntVolatile方法获取对象中offset偏移地址对应的整型field的值,支持volatile load语义。

    getLong方法获取对象中offset偏移地址对应的long型field的值

      对象的数组元素定位:arrayBaseOffset, arrayIndexScale

    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 {
        public static final int ARRAY_INT_BASE_OFFSET;
        public static final int ARRAY_INT_INDEX_SCALE;
    
        public native long staticFieldOffset(Field field);
        public native int getIntVolatile(Object obj, long l);
        public native long getLong(Object obj, long l);
        public native int arrayBaseOffset(Class class1);
        public native int arrayIndexScale(Class class1);
    
        static 
        {
            ARRAY_INT_BASE_OFFSET = theUnsafe.arrayBaseOffset([I);
            ARRAY_INT_INDEX_SCALE = theUnsafe.arrayIndexScale([I);
        }
    }

    复制代码

     

    4、CAS操作-Compare And Swap

    Unsafe中除了objectFieldOffset(Field) 这个方法外,还有一个类似的方法staticFieldOffset(Field)。这两个方法用于返回类定义中某个属性在主存中设定的偏移量。请看以下代码:

    复制代码

    // 注意本代码中的unsafe对象就是根据之前代码获取到的
    
    // 开始使用unsafe对象,分别找到UserPojo对象中child属性和name属性的内存地址偏移量
    // 首先是UserPojo类中的child属性,在内存中设定的偏移位置
    Field field = UserPojo.class.getDeclaredField("child");
    // 这就是一旦这个类实例化后,该属性在内存中的偏移位置
    long offset = unsafe.objectFieldOffset(field);
    System.out.println("child offset = " + offset);
    
    // 然后是UserPojo类中的name属性,在内存中设定的偏移位置
    Field fieldName = UserPojo.class.getDeclaredField("name");
    long nameOffset = unsafe.objectFieldOffset(fieldName);
    System.out.println("name offset = " + nameOffset);

    复制代码

    测试结果为 打印对应各个对象属性在主存中的偏移位置;具体结果不贴出来了;

     

    Unsafe中除了compareAndSwapObject 这个方法外,还有两个类似的方法:unsafe.compareAndSwapInt和unsafe.compareAndSwapLong。这些方法的作用就是对属性进行比较并替换(俗称的CAS过程——Compare And Swap)。当给定的对象中,指定属性的值符合预期,则将这个值替换成一个新的值并且返回true;否则就忽略这个替换操作并且返回false。

    请注意CAS过程是sun.misc.Unsafe类中除了获取内存偏移量以外,提供的最重要的功能了——因为Java中很多基于“无同步锁”方式的功能实现原理都是基于CAS过程。

      方法举例:CompareAndSwapInt()

    复制代码

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

    复制代码

    具体是代码应用实例:

    复制代码

    UserPojo user = new UserPojo();
    user.setName("yinwenjie");
    user.setSex(11);
    user.setUserId("userid");
    
    // 获得sex属性的内存地址偏移量 
    Field field = UserPojo.class.getDeclaredField("sex");
    long sexOffset = unsafe.objectFieldOffset(field);
    
    /* 
     * 比较并修改值
     * 1、需要修改的对象
     * 2、要更改的属性的内存偏移量
     * 3、预期的值
     * 4、设置的新值
     * */
    // 为什么是Object而不是int呢?因为sex属性的类型是Integer不是int嘛
    if(unsafe.compareAndSwapObject(user, sexOffset, 11, 13)) {
        System.out.println("更改成功!");
    } else {
        System.out.println("更改失败!");
    }

    复制代码

    首先创建一个UserPojo类的实例对象,这个实例对象有三个属性name、sex和userId。接着我们找到sex属性在主存中设定的偏移量sexOffset,并进行CAS操作。请注意compareAndSwapObject方法的四个值:第一个值表示要进行操作的对象user,第二个参数通过之前获取的主存偏移量sexOffset告诉方法将要比较的是user对象中的哪个属性,第三个参数为技术人员所预想的该属性的当前值,第四个参数为将要替换成的新值。

    那么将方法套用到以上的compareAndSwapObject执行过程中:如果当前user对象中sex属性为11,则将这个sex属性的值替换为13,并返回true;否则不替换sex属性的值,并且返回false。

     Unsafe.getAndAddInt(Object, long, int)和类似方法

    类似的方法还有getAndAddLong(Object, long, long),它们的作用是利用Unsafe的原子操作性,向调用者返回某个属性当前的值,并且紧接着将这个属性增加一个新的值。在java.util.concurrent.atomic代码包中,有一个类AtomicInteger,这个类用于进行基于原子操作的线程安全的计数操作,且这个类在JDK1.8+的版本中进行了较大的修改。以下代码示例了该类的getAndIncrement()方法中的实现片段:

    复制代码

    public class AtomicInteger extends Number implements java.io.Serializable {
        ……
        private volatile int value;
        ……
        private static final long valueOffset;
        ……
        // 获取到value属性的内存偏移量valueOffset
        static {
            try {
                valueOffset = unsafe.objectFieldOffset
                    (AtomicInteger.class.getDeclaredField("value"));
            } catch (Exception ex) { throw new Error(ex); }
        }
        ……
        /**
         * 这是JDK1.8中的实现
         * Atomically increments by one the current value.
         * @return the previous value
         */
        public final int getAndIncrement() {
            return unsafe.getAndAddInt(this, valueOffset, 1);
        }

    复制代码

     

    通过以上代码的演示可以看到,AtomicInteger类中定义了一个value属性,并通过unsafe.objectFieldOffset方法获取到了这个属性在主存中设定的偏移量valueOffset。接着就可以在getAndIncrement方法中直接使用unsafe.getAndAddInt的方式,通过偏移量valueOffset将value属性的值加“1”。但是该方法的实现在JDK1.8之前的版本中,实现代码却是这样的:

     

    复制代码

    // 获取偏移量valueOffset的代码类似,这里就不再展示了
    ……
    public final int getAndIncrement() {
        // 一直循环,直到
        for (;;) 
      {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }
    ……
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    复制代码

    这里采用的For循环进行原子atomicinteger操作;后边篇幅会写java的自旋锁以及自旋锁的一种具体实现方式“乐观锁”,还会分析“乐观锁”适合使用的场景。

    在以上代码中,getAndIncrement方法内部会不停的循环,直到unsafe.compareAndSwapInt方法执行成功。但多数情况下,循环只会执行一次,因为多线程强占同一对象属性的情况并不是随时都会出现。

    如果存在多线程进行访问这段for循环的代码 如果保证其结果是准确的呢,比如 100个线程执行 atomicinteger 的自增操作;

    下面用结合一个图来说明:

    我们看到如果三个线程来访问此基于乐观锁的代码时,首先线程3 率先到达 if 的地方 进行判断此时current的值为10,此时 其他的 线程2  线程3 都停留在get方法的地方 获取到了对象属性的值10

    此时如果线程3 根据预期值进行判断的过程中发现 current的值 与 对象属性value的值是一样的 此时会进行 CAS 操作,将对象的 value设置当前的 next 值 也就是 11; 然后线程3 执行结束  如果此时

    线程2 获得执行权限 继续往下执行  此时线程1 的 current值 还是为 10 next的值为11 来到if的地方进行判断 此时 因为对象的属性value的值是11 与预期值 current不等 所以不行CAS操作; 然后继续进行新一轮的

    for循环 此时 继续get取得 current的值 是 11 next的值我 12  此时进行if 测试的预期值和对象的value值是一样的 然后执行更新操作;此时对象value的值被更新为12 线程2执行完毕,然后就剩下了线程 1 该线程的执行与线程2

    的执行时一样的 需要进行一次新的for循环 才可以实现更新值得操作;

    从上面的过程可以看出,三个线程可以完成三次更新值得操作,并且没有加入同步锁。

     

    在JDK1.8中Unsafe还有一些其它实用的原子操作方法:

    • PutXXXXX(Object, long, short)

      类似的方法包括:putInt(Object, long, int)、putBoolean(Object, long, boolean)、putShort(Object, long, short)、putChar(Object, long, char)、putDouble(Object, long, double)等,这些都是针对指定对象中在偏移量上的属性值,进行直接设定。这些操作发生在CPU一级缓存(L1) 或者二级缓存(L2)中,但是这些方法并不保证工作在其它内核上的线程“立即看到”最新的属性值。

    • putXXXXXVolatile(Object, long, byte)

      类似的方法包括:putByteVolatile(Object, long, byte)、putShortVolatile(Object, long, short)、putFloatVolatile(Object, long, float)、putDoubleVolatile(Object, long, double)等等,这些方法的主要作用虽然也是直接针对偏移量改变指定对象中的属性值,但是这些方法保证工作在其它内核上的线程能“立即看到”最新的属性值——也就是说这些方法满足volatile语义(后续文章会详细介绍volatile的详细工作原理)

     

    总结:

    Unsafe类的总结已经写完 ,基于该类使用的类有很多,除了原子数据 AtomicXXX, 还有LockSupport类 以及在 线程池 ThreadPool 类也是用了该类, 后边具体写这两个类。

    展开全文
  • unsafe类

    2020-10-14 15:35:36
    Java中的Unsafe类为我们提供了类似C++手动管理内存的能力。 Unsafe类,全限定名是sun.misc.Unsafe,从名字中我们可以看出来这个类对普通程序员来说是“危险”的,一般应用开发者不会用到这个类。 Unsafe类是"final...

    1.简介

    Java和C++语言的一个重要区别就是Java中我们无法直接操作一块内存区域,不能像C++中那样可以自己申请内存和释放内存。Java中的Unsafe类为我们提供了类似C++手动管理内存的能力。
    Unsafe类,全限定名是sun.misc.Unsafe,从名字中我们可以看出来这个类对普通程序员来说是“危险”的,一般应用开发者不会用到这个类。

    Unsafe类是"final"的,不允许继承。且构造函数是private的,在外部对Unsafe进行实例化。
    获取Unsafe
    Unsafe无法实例化,那么怎么获取Unsafe呢?答案就是通过反射来获取Unsafe:

    public Unsafe getUnsafe() throws IllegalAccessException {
        Field unsafeField = Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe = (Unsafe) unsafeField.get(null);
        return unsafe;
    }
    

    2.主要功能

    Unsafe的功能如下图:
    Unsafe-xmind

    2.1普通读写

    通过Unsafe可以读写一个类的属性,即使这个属性是私有的,也可以对这个属性进行读写。

    读写一个Object属性的相关方法

    public native int getInt(Object var1, long var2);
    
    public native void putInt(Object var1, long var2, int var4);
    

    getInt用于从对象的指定偏移地址处读取一个int。putInt用于在对象指定偏移地址处写入一个int。其他的primitive type也有对应的方法。

    Unsafe还可以直接在一个地址上读写

    public native byte getByte(long var1);
    
    public native void putByte(long var1, byte var3);
    

    getByte用于从指定内存地址处开始读取一个byte。putByte用于从指定内存地址写入一个byte。其他的primitive type也有对应的方法。

    2.2 volatile读写

    普通的读写无法保证可见性和有序性,而volatile读写就可以保证可见性和有序性。

    public native int getIntVolatile(Object var1, long var2);
    
    public native void putIntVolatile(Object var1, long var2, int var4);
    

    getIntVolatile方法用于在对象指定偏移地址处volatile读取一个int。putIntVolatile方法用于在对象指定偏移地址处volatile写入一个int。

    volatile读写相对普通读写是更加昂贵的,因为需要保证可见性和有序性,而与volatile写入相比putOrderedXX写入代价相对较低,putOrderedXX写入不保证可见性,但是保证有序性,所谓有序性,就是保证指令不会重排序。

    2.3 有序写入

    有序写入只保证写入的有序性,不保证可见性,就是说一个线程的写入不保证其他线程立马可见。

    public native void putOrderedObject(Object var1, long var2, Object var4);
    
    public native void putOrderedInt(Object var1, long var2, int var4);
    
    public native void putOrderedLong(Object var1, long var2, long var4);
    

    2.4 直接内存操作

    我们都知道Java不可以直接对内存进行操作,对象内存的分配和回收都是由JVM帮助我们实现的。但是Unsafe为我们在Java中提供了直接操作内存的能力。

    // 分配内存
    public native long allocateMemory(long var1);
    // 重新分配内存
    public native long reallocateMemory(long var1, long var3);
    // 内存初始化
    public native void setMemory(long var1, long var3, byte var5);
    // 内存复制
    public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);
    // 清除内存
    public native void freeMemory(long var1);
    

    2.5 CAS相关

    JUC中大量运用了CAS操作,可以说CAS操作是JUC的基础,因此CAS操作是非常重要的。Unsafe中提供了int,long和Object的CAS操作:

    
    public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
    
    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
    
    public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
    

    CAS一般用于乐观锁,它在Java中有广泛的应用,ConcurrentHashMap,ConcurrentLinkedQueue中都有用到CAS来实现乐观锁。

    2.6 偏移量相关

    public native long staticFieldOffset(Field var1);
    
    public native long objectFieldOffset(Field var1);
    
    public native Object staticFieldBase(Field var1);
    
    public native int arrayBaseOffset(Class<?> var1);
    
    public native int arrayIndexScale(Class<?> var1);
    

    staticFieldOffset方法用于获取静态属性Field在对象中的偏移量,读写静态属性时必须获取其偏移量。objectFieldOffset方法用于获取非静态属性Field在对象实例中的偏移量,读写对象的非静态属性时会用到这个偏移量。staticFieldBase方法用于返回Field所在的对象。arrayBaseOffset方法用于返回数组中第一个元素实际地址相对整个数组对象的地址的偏移量。arrayIndexScale方法用于计算数组中第一个元素所占用的内存空间。

    2.7 线程调度

    public native void unpark(Object var1);
    
    public native void park(boolean var1, long var2);
    
    public native void monitorEnter(Object var1);
    
    public native void monitorExit(Object var1);
    
    public native boolean tryMonitorEnter(Object var1);
    park方法和unpark方法相信看过LockSupport类的都不会陌生,这两个方法主要用来挂起和唤醒线程。LockSupport中的park和unpark方法正是通过Unsafe来实现的:
    
    // 挂起线程
    public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker); // 通过Unsafe的putObject方法设置阻塞阻塞当前线程的blocker
        UNSAFE.park(false, 0L); // 通过Unsafe的park方法来阻塞当前线程,注意此方法将当前线程阻塞后,当前线程就不会继续往下走了,直到其他线程unpark此线程
        setBlocker(t, null); // 清除blocker
    }
    
    // 唤醒线程
    public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }
    

    monitorEnter方法和monitorExit方法用于加锁,Java中的synchronized锁就是通过这两个指令来实现的。

    2.8 类加载

    public native Class<?> defineClass(String var1, byte[] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6);
    
    public native Class<?> defineAnonymousClass(Class<?> var1, byte[] var2, Object[] var3);
    
    public native Object allocateInstance(Class<?> var1) throws InstantiationException;
    
    public native boolean shouldBeInitialized(Class<?> var1);
    
    public native void ensureClassInitialized(Class<?> var1);
    

    defineClass方法定义一个类,用于动态地创建类。
    defineAnonymousClass用于动态的创建一个匿名内部类。
    allocateInstance方法用于创建一个类的实例,但是不会调用这个实例的构造方法,如果这个类还未被初始化,则初始化这个类。
    shouldBeInitialized方法用于判断是否需要初始化一个类。
    ensureClassInitialized方法用于保证已经初始化过一个类。

    2.9 内存屏障

    public native void loadFence();
    
    public native void storeFence();
    
    public native void fullFence();
    

    loadFence:保证在这个屏障之前的所有读操作都已经完成。
    storeFence:保证在这个屏障之前的所有写操作都已经完成。
    fullFence:保证在这个屏障之前的所有读写操作都已经完成。

    展开全文
  • UnSafe类

    2019-04-29 19:41:19
    参考:https://www.cnblogs.com/pkufork/p/java_unsafe.html 内存管理。包括分配内存、释放内存等。...操作、对象、变量。 数组操作。 多线程同步。包括锁机制、CAS操作等。 挂起与恢复。 内存屏障。 ...

    参考:https://www.cnblogs.com/pkufork/p/java_unsafe.html

    1. 内存管理。包括分配内存、释放内存等。
    2. 非常规的对象实例化。
    3. 操作类、对象、变量。
    4. 数组操作。
    5. 多线程同步。包括锁机制、CAS操作等。
    6. 挂起与恢复。
    7. 内存屏障。
    展开全文
  • Java并发包的源码中有着Unsafe类Unsafe类是在sun.misc包下,不属于Java标准。但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty、Cassandra、Hadoop、Kafka等。Unsafe...

    Java并发包的源码中有着Unsafe类。

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

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

    通常我们最好也不要使用Unsafe类,除非有明确的目的,并且也要对它有深入的了解才行。要想使用Unsafe类需要用一些比较tricky的办法。Unsafe类使用了单例模式,需要通过一个静态方法getUnsafe()来获取。但Unsafe类做了限制,如果是普通的调用的话,它会抛出一个SecurityException异常;只有由主类加载器加载的类才能调用这个方法。其源码如下:

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    1 public staticUnsafe getUnsafe() {

    2 Class var0 =Reflection.getCallerClass();

    3 if(!VM.isSystemDomainLoader(var0.getClassLoader())) {

    4 throw new SecurityException("Unsafe");

    5 } else{

    6 returntheUnsafe;

    7 }

    8 }

    48304ba5e6f9fe08f3fa1abda7d326ab.png

    网上也有一些办法来用主类加载器加载用户代码,比如设置bootclasspath参数。但更简单方法是利用Java反射,方法如下:

    1 Field f = Unsafe.class.getDeclaredField("theUnsafe");

    2 f.setAccessible(true);

    3 Unsafe unsafe = (Unsafe) f.get(null);

    获取到Unsafe实例之后,我们就可以为所欲为了。Unsafe类提供了以下这些功能:

    一、内存管理。包括分配内存、释放内存等。

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

    利用copyMemory方法,我们可以实现一个通用的对象拷贝方法,无需再对每一个对象都实现clone方法,当然这通用的方法只能做到对象浅拷贝。

    二、非常规的对象实例化。

    allocateInstance()方法提供了另一种创建实例的途径。通常我们可以用new或者反射来实例化对象,使用allocateInstance()方法可以直接生成对象实例,且无需调用构造方法和其它初始化方法。

    这在对象反序列化的时候会很有用,能够重建和设置final字段,而不需要调用构造方法。

    三、操作类、对象、变量。

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

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

    四、数组操作。

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

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

    五、多线程同步。包括锁机制、CAS操作等。

    这部分包括了monitorEnter、tryMonitorEnter、monitorExit、compareAndSwapInt、compareAndSwap等方法。

    其中monitorEnter、tryMonitorEnter、monitorExit已经被标记为deprecated,不建议使用。

    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的主要功能总结1、Unsafe类介绍第一次看到这个类时被它的名字吓到了,居然还有一个类自名Unsafe?读完本文,大家也能发现Unsafe类确实有点不那么安全,它能实现一些不那么常见的功能...
  • Java Unsafe类的使用Unsafe类的作用Unsafe类是rt.jar包中的类,它提供了原子级别的操作,它的方法都是native方法,通过JNI访问本地的C++库。它的出现是为了解决在高并发下的数据同步问题。synchronize关键字修饰的...
  • Unsafe类提供了硬件级别的原子操作,主要提供了以下功能:1、通过Unsafe类可以分配内存,可以释放内存;类中提供的3个本地方法allocateMemory、reallocateMemory、freeMemory分别用于分配内存,扩充内存和释放内存,...
  • 前言本文主要给大家介绍了关于Java中Unsafe类的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧1.Unsafe类介绍Unsafe类是在sun.misc包下,不属于Java标准。但是很多Java的基础类库,包括...
  • Unsafe类学习

    2019-10-20 10:29:00
    Unsafe类学习(了解) 介绍 java 不能直接访问操作系统底层,而是通过native方法来访问。Unsafe 类提供了硬件级别的原子操作。 Unsafe 类在 sun.misc 包下,不属于 Java 标准。很多 Java 的基础类库,包括一些被广泛...
  • JUC基石——Unsafe类

    2020-12-21 06:49:03
    我们经常在JUC包下的ConcurrentHashMap、Atomic开头的原子操作类、AQS以及LockSupport里面看到Unsafe类的身影,这个Unsafe类究竟是干什么的,本文可以带着读者一探究竟。 Java和C++、C语言的一个重要区别,就是Java...
  • 通常Java代码都是运行在JVM中而不能直接访问...1、Unsafe说明Unsafe类在sun.misc包下,不属于Java标准。但是很多Java基础类库,包括一些高性能的开发库都是基于Unsafe类开发的,比如Netty、Hadoop、Kafka、JUC并发...
  • Unsafe类详解

    2019-07-31 21:50:25
    Unsafe类就和它的名字一样,是一个比较危险的类,它主要用于执行低级别、不安全的方法。尽管这个类和所有的方法都是公开的(public),但是这个类的使用仍然受限,你无法在自己的java程序中直接使用该类,因为只有...
  • Unsafe类的介绍 Java中基于操作系统级别的原子操作类sun.misc.Unsafe,它是Java中对大多数锁机制实现的最基础类。请注意,JDK 1.8和之前JDK版本的中sun.misc.Unsafe类可提供的方法有较大的变化,本文基于JDK 1.8。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,879
精华内容 1,151
关键字:

unsafe类