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和反射的区别。