反射机制和使用Unsafe方法对比

package reflectionDemo;

import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;


public class ThreadTest {
    static Field fThreadLocalRandomSeed ;
    static Unsafe UNSAFE;
    private static long SEED;

    static {
        // 反射机制 获取属性
        try {
            fThreadLocalRandomSeed = Thread.class.getDeclaredField("threadLocalRandomSeed");
            fThreadLocalRandomSeed.setAccessible(true);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }


        PrivilegedAction action = () -> {
            try {
                Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
                theUnsafe.setAccessible(true);
                return (Unsafe) theUnsafe.get(null);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
                return null;
            }
        };
        UNSAFE = (Unsafe) AccessController.doPrivileged(action);

        // 通过unsafe获取 属性
        try {
            SEED = UNSAFE.objectFieldOffset(Thread.class.getDeclaredField("threadLocalRandomSeed"));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

// 使用反射机制
    public static void byReflection() throws IllegalAccessException {
        long b = System.currentTimeMillis();
        Thread thread = Thread.currentThread();
        fThreadLocalRandomSeed.set(thread,0);
        for (int i = 0; i < 100000000 ; i++) {
            fThreadLocalRandomSeed.set(thread,(Long)fThreadLocalRandomSeed.get(thread)+1);
        }
        long e = System.currentTimeMillis();
        System.out.println("byReflection spend:"+ (b-e)+"ms");
    }

// 使用unsafe方法
    public static void byUnsafe() throws IllegalAccessException{
        long b = System.currentTimeMillis();
        UNSAFE.putLong(Thread.currentThread(),SEED,0);
        Thread thread = Thread.currentThread();
        for (int i = 0; i < 100000000 ; i++) {
            UNSAFE.putLong(thread,SEED,UNSAFE.getLong(thread,SEED)+1);
        }
        long e = System.currentTimeMillis();
        System.out.println("byUnsafe spend:"+ (b-e)+"ms");
    }

    public static void main(String[] args) throws IllegalAccessException {
       /* for (int i = 0; i < 10 ; i++) {
            byReflection();
            byUnsafe();
        }*/
        System.out.println("======================");
        byReflection();
        byUnsafe();
    }
}

反射的高效替代方案
随机数的产生需要访问Thread的threadLocalRandomSeed等成员,但是考虑到类的封装性,这些成员却是包内可见的。

很不幸,ThreadLocalRandom位于java.util.concurrent包,而Thread则位于java.lang包,因此,ThreadLocalRandom并没有办法访问Thread的threadLocalRandomSeed等变量。

反射是一种可以绕过封装,直接访问对象内部数据的方法,但是,反射的性能不太好,并不适合作为一个高性能的解决方案。

有没有什么办法可以让ThreadLocalRandom访问Thread的内部成员,同时又具有远超于反射的,且无限接近于直接变量访问的方法呢?答案是肯定的,这就是使用Unsafe类。

这里,就简单介绍一下用的两个Unsafe的方法:

public native long    getLong(Object o, long offset);
public native void    putLong(Object o, long offset, long x);

其中getLong()方法,会读取对象o的第offset字节偏移量的一个long型数据;putLong()则会将x写入对象o的第offset个字节的偏移量中。

这类类似C的操作方法,带来了极大的性能提升,更重要的是,由于它避开了字段名,直接使用偏移量,就可以轻松绕过成员的可见性限制了。

性能问题解决了,那下一个问题是,我怎么知道threadLocalRandomSeed成员在Thread中的偏移位置呢,这就需要用unsafe的objectFieldOffset()方法了,请看下面的代码:

上述这段static代码,在ThreadLocalRandom类初始化的时候,就取得了Thread成员变量threadLocalRandomSeed,threadLocalRandomProbe,threadLocalRandomSecondarySeed在对象偏移中的位置。

因此,只要ThreadLocalRandom需要使用这些变量,都可以通过unsafe的getLong()和putLong()来进行访问(也可能是getInt()和putInt())。

比如在生成一个随机数的时候:

 protected int next(int bits) {
        return (int)(mix64(nextSeed()) >>> (64 - bits));
    }
    final long nextSeed() {
        Thread t; long r; // read and update per-thread seed
        //在ThreadLocalRandom中,访问了Thread的threadLocalRandomSeed变量
        UNSAFE.putLong(t = Thread.currentThread(), SEED,
                       r = UNSAFE.getLong(t, SEED) + GAMMA);
        return r;
    }

这种Unsafe的方法掉地能有多快呢,让我们一起看做个试验看看:

这里,我们自己写一个ThreadTest类,使用反射和unsafe两种方法,来不停读写threadLocalRandomSeed成员变量,比较它们的性能差异,代码如下:

上述代码中,分别使用反射方式byReflection() 和Unsafe的方式byUnsafe()来读写threadLocalRandomSeed变量1亿次,得到的测试结果如下:


不难看到,使用Unsafe的方法远远优于反射的方法,这也是JDK内部,大量使用Unsafe来替代反射的原因之一。


原文链接:https://blog.csdn.net/qq_35190492/article/details/115450889

https://blog.csdn.net/mayifan_blog/article/details/86777656

https://blog.csdn.net/weixin_34096182/article/details/88678646

通过原文 自己测试了一下  记录一下使用unsafe和反射的区别。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值