Unsafe的使用解析
2018-12-10 本文已影响0人
braveheart075
相信很多人都阅读过jdk源码,这两天在看一段代码都时候正好用到Unsafe(JRE的rt.jar中提供了一个类sun.misc.Unsafe),特地研究了下Unsafe这个类。基于对象内存来进行操作对象。不安全。所以不建议使用。特别在concurrent包中都Atomicxxx类,底层都CAS 都用到了这个类。那么这个类究竟能干什么,我们先从实例化开始:
- 首先,这个类是不能直接实例化到,但是可以通过反射来获取。
sun.misc.Unsafe U = null;
Field theUnsafeInstance = null;
try {
theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeInstance.setAccessible(true);
U = (Unsafe) theUnsafeInstance.get(Unsafe.class);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
-
其次,我们一般会用到这个类到compareAndSwapXXX,这个方法是干嘛用的?官网解释如下:
Screen Shot 2018-12-10 at 9.43.36 PM.png
就是根据对象在内存的offset(我们叫他偏移量),来比较你期望的值和内存值是否相等,如果相等返回true,并进行swap,用最后一个x来进行swap。
-
那么,我们来演示下:
建一个对象:
public class Main {
private String s;
private long s1;
private long s2;
public static void main(String[] args) {
Main main = new Main();
main.s1 = 20;
long s, next;
long state = 256;
System.out.println(1 << 6);
System.out.println(1 << 10);
System.out.println(1L << 7);
System.out.println("Hello World!");
long except = 1;
long real = 1;
sun.misc.Unsafe U = null;
Field theUnsafeInstance = null;
try {
theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeInstance.setAccessible(true);
U = (Unsafe) theUnsafeInstance.get(Unsafe.class);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
System.out.println("========");
// Arrays.stream(Main.class.getDeclaredFields()).forEach(field -> System.out.println(U.objectFieldOffset(field.getName())));
Field[] fields = Main.class.getDeclaredFields();
for(Field field : fields) {
System.out.println(field.getName()+ "=="+ U.objectFieldOffset(field));
}
System.out.println("========");
boolean flag = U.compareAndSwapLong(main,16,21,21);
System.out.println(flag);
System.out.println(main.s1);
}
}
- 我们看到了这个代码,有三个field。如何获得这三个field?如下这段代码发挥作用:
Field[] fields = Main.class.getDeclaredFields();
- 获取对象的偏移量:
for(Field field : fields) {
System.out.println(field.getName()+ "=="+ U.objectFieldOffset(field));
}
打印出来的结果如下:
s==12
s1==16
s2==24
这个数字就是offset。那么我们可以验证这个compare方法了。
- 验证compare方法:我们将offset为16的内存变量s1改成21(原先是20)
boolean flag = U.compareAndSwapLong(main,16,20,21);
System.out.println(flag);
System.out.println(main.s1);
就是比较偏移量为16的内存对象值是否是20,如果是,改成21,打印结果如下:
true
21
可以看到,如预期。好了,大家都知道类似的方法怎么用了吧。期望对大家有帮助。