第一次知道Unsafe概念是在使用ReentrntLock和CountDownLatch 。这两个都实现了AQS ,AQS能够做到线程安全同步,关键在于AQS中通过查询state变量的值来知道当前资源有没有被锁,改变state变量的值来表示当前资源被锁,改变state是要保证原子性 的,原子性保障由Unsafe中的CompareAntSwap支持。
从ReentrantLock的非公平锁开始看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L ; final void lock () { if (compareAndSetState(0 , 1 )) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1 ); } protected final boolean tryAcquire (int acquires) { return nonfairTryAcquire(acquires); } }
lock方法主要是在compareAndSetState(0,1)成功后再让本线程进来执行任务。
1 2 3 4 protected final boolean compareAndSetState (int expect, int update) { return unsafe.compareAndSwapInt(this , stateOffset, expect, update); }
1 2 3 stateOffset = unsafe.objectFieldOffset (AbstractQueuedSynchronizer.class.getDeclaredField("state" ));
所以,ReentrantLock在上锁时一直在维护一个state变量, 想获得锁必须做compareAndSwap比较与交换的操作。
这个"比较与交换"是用Unsafe实现的:
1 2 3 4 5 6 7 protected final boolean compareAndSetState (int expect, int update) { return unsafe.compareAndSwapInt(this , stateOffset, expect, update); } public final native boolean compareAndSwapInt (Object var1, long var2, int var4, int var5) ;
还想说一下,什么是原子性,compareAndSwap比较与交换有多个指令组成,这些指令中途不能被打断,整个一起执行完,这就是原子性。
上面是结合ReentrantLock的场景。
Unsafe类非常有意思:
它是根据变量在对象中的偏移量来改变值的,非常底层,非常c语言指针的感觉,但由保证了原子性。
构造方法是private, 程序员不能new过来用, 只能由最底层的system级别的类加载器加载的类才可以通过getUnsafe()方法获取用。(加载器由三种:引导类加载器,ExtClassLoader,AppClassLoader。除了jdk自带的那些底层类,其他由开发者编写的类都被AppClassLoader加载,所以想调用getUnsafe获取unsafe做操作,那你必须是jdk自带的那些底层类) 以下是关键代码:
1 2 3 4 5 6 7 8 9 10 11 12 private Unsafe () { } @CallerSensitive public static Unsafe getUnsafe () { Class var0 = Reflection.getCallerClass(); if (!VM.isSystemDomainLoader(var0.getClassLoader())) { throw new SecurityException ("Unsafe" ); } else { return theUnsafe; } }
unsafe意味着危险,因为它操作内存的权限比较大,给底层开发jdk的人去用,实现这些concurrent里面的那些原子操作。
当然我们也可以拿来做做实验体会一下,因为还可以通过反射机制来获取unsafe使用:
demo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class UnsafeFactory { public static Unsafe getUnsafe () throws NoSuchFieldException, IllegalAccessException { Field field = Unsafe.class.getDeclaredField("theUnsafe" ); field.setAccessible(true ); return (Unsafe)field.get(null ); } public static long getFieldOffset (Unsafe unsafe, Field field) { long offset = unsafe.objectFieldOffset(field); return offset; } public static void main (String[] args) throws NoSuchFieldException, IllegalAccessException { Unsafe unsafe = getUnsafe(); Field num = ThreadPlusPlusProblem.class.getDeclaredField("num" ); long fieldOffset = getFieldOffset(unsafe, num); System.out.println("num在ThreadPlusPlusProblem中的偏移量:" +fieldOffset); ThreadPlusPlusProblem threadPlusPlusProblem = new ThreadPlusPlusProblem (); num.setAccessible(true ); num.set(threadPlusPlusProblem,1 ); System.out.println(JSON.toJSONString(threadPlusPlusProblem)); } }
建议从ReentrantLock.lock()点进去看一下怎么通过unsafe来原子性的维护state变量来实现锁状态的查询和改变,一定要读AQS这里的源码,想一下AQS中的state用volatile修饰,这里的用意是很底层和比较cool。
希望你是非常喜欢自己的工作, 从中能找到意义和成就^^