【揭秘】JUC并發工具包底層機制探究,Unsafe原來這么強大!
Unsafe介紹
Unsafe是位于sun.misc包下的一個類,主要提供一些用于執行低級別、不安全操作的方法,如直接訪問系統內存資源、自主管理內存資源等,這些方法在提升Java運行效率、增強Java語言底層資源操作能力方面起到了很大的作用。但由于Unsafe類使得Java語言擁有了類似C語言指針一樣操作內存空間的能力,這無疑也增加了程序發生相關指針問題的風險。在程序中過度、不正確使用Unsafe類會使得程序出錯的概率變大,使得Java這種安全的語言變得不再“安全”,因此對Unsafe的使用一定要慎重。java.util.concurrent.atomic包下的原子操作類,基本都是使用Unsafe實現的。
Unsafe提供的API大致可分為內存操作、CAS、Class、對象操作、線程、系統信息獲取、內存屏障、數組操作等幾類。
內存相關
圖片
CAS相關
圖片
java.util.concurrent.atomic包中的原子類基本都用的Unsafe
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue);
}
線程相關
圖片
LockSupport類中有應用unpark,park
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
Class相關
圖片
對象操作相關
圖片
圖片
系統相關
圖片
內存屏障
圖片
loadFence:保證在這個屏障之前的所有讀操作都已經完成。
storeFence:保證在這個屏障之前的所有寫操作都已經完成。fullFence:保證在這個屏障之前的所有讀寫操作都已經完成。
在java8中 有這個StampedLock類,該類中應用了內存屏障功能。
private static final sun.misc.Unsafe U;
static {
try {
U = sun.misc.Unsafe.getUnsafe();
} catch (Exception e) {
throw new Error(e);
}
}
public boolean validate(long stamp) {
U.loadFence();
return (stamp & SBITS) == (state & SBITS);
}
Unsafe.java
public final class Unsafe {
private static native void registerNatives();
static {
registerNatives();
sun.reflect.Reflection.registerMethodsToFilter(Unsafe.class, "getUnsafe");
}
private Unsafe() {}
private static final Unsafe theUnsafe = new Unsafe();
// ...
}
獲取Unsafe實例
Unsafe類是final且是單例的,并且theUnsafe字段是private;通過如下方法獲取實例。
方法1
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe") ;
theUnsafe.setAccessible(true) ;
Unsafe unsafe = (Unsafe) theUnsafe.get(null) ;
方法2
private static Unsafe unsafe = null ;
static {
try {
Constructor<Unsafe> cons = Unsafe.class.getDeclaredConstructor() ;
cons.setAccessible(true) ;
unsafe = cons.newInstance() ;
} catch (Exception e) {
e.printStackTrace();
}
}
Unsafe簡單應用
int i = 0 ;
public static void main(String[] args) throws Exception {
UnsafeDemo d = new UnsafeDemo() ;
// 獲取Unsafe實例
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe") ;
theUnsafe.setAccessible(true) ;
Unsafe unsafe = (Unsafe) theUnsafe.get(null) ;
// 獲取類的實例變量
Field f = UnsafeDemo.class.getDeclaredField("i") ;
// 獲取字段相對Java對象的"起始地址"的偏移量
long fieldOffset = unsafe.objectFieldOffset(f) ;
System.out.println(fieldOffset) ;
// 設置值
boolean success = unsafe.compareAndSwapInt(d, fieldOffset, 0, 10) ;
System.out.println(success) ;
System.out.println(d.i) ;
}
Unsafe對象操作
private static Unsafe unsafe = null ;
static {
try {
Constructor<Unsafe> cons = Unsafe.class.getDeclaredConstructor() ;
cons.setAccessible(true) ;
unsafe = cons.newInstance() ;
} catch (Exception e) {
e.printStackTrace();
}
}
public static void allocate() {
try {
Person p = (Person)unsafe.allocateInstance(Person.class) ;
p.setId("s001");
System.out.println(p.getValue()) ;
System.out.println(p.getId()) ;
} catch (Exception e) {
e.printStackTrace();
}
}
執行結果:
圖片
對象操作2:
private Person p = new Person("1", "張三") ;
public static void main(String[] args) throws Exception {
UnSafeObjectDemo d = new UnSafeObjectDemo() ;
Field field = Unsafe.class.getDeclaredField("theUnsafe") ;
field.setAccessible(true) ;
Unsafe unsafe = (Unsafe) field.get(null) ;
Field f = d.getClass().getDeclaredField("p") ;
long offset = unsafe.objectFieldOffset(f) ;
System.out.println(offset) ;
boolean res = unsafe.compareAndSwapObject(d, offset, d.p, new Person("2", "李四")) ;
System.out.println(res) ;
System.out.println(d.p.getName()) ;
}
圖片
Unsafe創建對象
當不知道即將使用的對象有何構造函數,或是不想使用現有對象的構造函數創建對象時,可以通過如下方式:
Constructor<Teacher> cons = (Constructor<Teacher>) ReflectionFactory
.getReflectionFactory()
.newConstructorForSerialization(Teacher.class, Object.class.getConstructor());
cons.setAccessible(true) ;
Teacher t = cons.newInstance() ;
System.out.println(t) ;
Unsafe簡單實現原子操作類
public class AtomicCount {
private static Unsafe unsafe ;
private int value ;
private static long valueOffset ;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe") ;
theUnsafe.setAccessible(true) ;
unsafe = (Unsafe) theUnsafe.get(null) ;
Field f = AtomicCount.class.getDeclaredField("value") ;
valueOffset = unsafe.objectFieldOffset(f) ;
} catch (Exception e) {
e.printStackTrace();
}
}
public AtomicCount(int value) {
this.value = value ;
}
public final int get() {
return value;
}
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
}
完畢!!!