前言
- Unsafe顧名思義,它不安全,要小心使用
- Unsafe可以控制對象的內存申請和釋放,可以對內存訪問進行控制
- Unsafe本身僅是爲JDK服務的,不推薦應用程序直接使用,且JDK可能隨時會改動它
以下演示的JDK版本:1.8
1 使用
部分源碼:
Unsafe:
//私有變量
private static final Unsafe theUnsafe;
//私有構造函數
private Unsafe(){}
//私有變量初始化
static {
......
theUnsafe = new Unsafe();
......
}
//公開獲取Unsafe對象
public static Unsafe getUnsafe() {
Class arg = Reflection.getCallerClass();
//判斷當前ClassLoader是否是頂層類加載器(null),所以一般情況下,只有rt.jar中的類可以使用Unsafe對象
if (!VM.isSystemDomainLoader(arg.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
VM:
//判斷ClassLoader是否是頂層類加載器(null)
public static boolean isSystemDomainLoader(ClassLoader arg) {
return arg == null;
}
分析:
- Unsafe類是final類型,無法被其他類繼承
- Unsafe構造函數爲私有,不能通過常規途徑創建對象
- Unsafe成員變量theUnsafe爲私有,無法被外部訪問
- 唯一的公開方法getUnsafe,限制了只有rt.jar才能使用(或者是-Xbootclasspath指定class)
那咋辦?只能靠反射了,通過構造函數或成員變量,都能獲取Unsafe對象:
/**
* 通過私有構造函數Unsafe()獲取Unsafe
*
* @return
* @throws Exception
*/
public static Unsafe getUnsafeByContructor() throws Exception {
// 獲取私有無參構造函數Unsafe()
Constructor<Unsafe> constructor = Unsafe.class.getDeclaredConstructor();
// 關閉安全檢查,不代表一定可以訪問,但一定可以減小安全檢查的資源消耗,常用於反射類功能中
constructor.setAccessible(true);
Unsafe unsafe = (Unsafe) constructor.newInstance();
return unsafe;
}
/**
* 通過私有成員變量theUnsafe獲取Unsafe對象
*
* @return
* @throws Exception
*/
public static Unsafe getUnsafeByField() throws Exception {
// 獲取私有成員變量theUnsafe
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
// 關閉安全檢查,不代表一定可以訪問,但一定可以減小安全檢查的資源消耗,常用於反射類功能中
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
return unsafe;
}
2 併發安全
2.1 CAS
一種可以支持併發修改的判斷:
//1:待更新對象;2:內存地址-偏移量;3:當前預期值;4:待更新值;
//更新成功返回true,否則爲false
public final native boolean compareAndSwapObject(Object arg0, long arg1, Object arg3, Object arg4);
public final native boolean compareAndSwapInt(Object arg0, long arg1, int arg3, int arg4);
public final native boolean compareAndSwapLong(Object arg0, long arg1, long arg3, long arg5);
2.2 volatile
Object類型和8種基礎數據類型的18個Get和Put方法,包含volatile特性:工作內存中的值永遠最新;禁止指令重排。
public native Object getObjectVolatile(Object arg0, long arg1);
public native void putObjectVolatile(Object arg0, long arg1, Object arg3);
public native int getIntVolatile(Object arg0, long arg1);
public native void putIntVolatile(Object arg0, long arg1, int arg3);
public native boolean getBooleanVolatile(Object arg0, long arg1);
public native void putBooleanVolatile(Object arg0, long arg1, boolean arg3);
public native byte getByteVolatile(Object arg0, long arg1);
public native void putByteVolatile(Object arg0, long arg1, byte arg3);
public native short getShortVolatile(Object arg0, long arg1);
public native void putShortVolatile(Object arg0, long arg1, short arg3);
public native char getCharVolatile(Object arg0, long arg1);
public native void putCharVolatile(Object arg0, long arg1, char arg3);
public native long getLongVolatile(Object arg0, long arg1);
public native void putLongVolatile(Object arg0, long arg1, long arg3);
public native float getFloatVolatile(Object arg0, long arg1);
public native void putFloatVolatile(Object arg0, long arg1, float arg3);
public native double getDoubleVolatile(Object arg0, long arg1);
public native void putDoubleVolatile(Object arg0, long arg1, double arg3);
2.3 CAS+Volatile
//給對象增加指定值:1-對象;2-內存地址;3-增加值
public final int getAndAddInt(Object arg0, long arg1, int arg3) {
int arg4;
do {
arg4 = this.getIntVolatile(arg0, arg1);
} while (!this.compareAndSwapInt(arg0, arg1, arg4, arg4 + arg3));
return arg4;
}
public final long getAndAddLong(Object arg0, long arg1, long arg3) {
......
}
//給對象賦予指定值:1-對象;2-內存地址;3-賦予值
public final int getAndSetInt(Object arg0, long arg1, int arg3) {
int arg4;
do {
arg4 = this.getIntVolatile(arg0, arg1);
} while (!this.compareAndSwapInt(arg0, arg1, arg4, arg3));
return arg4;
}
public final long getAndSetLong(Object arg0, long arg1, long arg3) {
......
}
public final Object getAndSetObject(Object arg0, long arg1, Object arg3) {
......
}
2.4 內存屏障
//內存屏障,禁止load操作重排序,即屏障前的load操作不能被重排序到屏障後,屏障後的load操作不能被重排序到屏障前
//即a=1;loadFence();b=2;那麼工作內存中一定是先發生a=1,然後再發生b=2;
public native void loadFence();
//內存屏障,禁止store操作重排序,即屏障前的store操作不能被重排序到屏障後,屏障後的store操作不能被重排序到屏障前
//即a=1;storeFence();b=2;那麼主內存中一定是先發生a=1,然後再發生b=2;工作內存中,可能先b=2,後a=1;
public native void storeFence();
//內存屏障,禁止load、store操作重排序
//即a=1;fullFence();b=2;那麼工作內存和主內存都是先發生a=1,然後再發生b=2;
public native void fullFence();
volatile和內存屏障不瞭解的可以看:java內存模型與線程
2.5 有序寫入
//設置對象var1中offset偏移地址var2對應的Object型field的值爲指定值var4
public native void putOrderedObject(Object var1, long var2, Object var4);
public native void putOrderedInt(Object var1, long var2, int var4);
public native void putOrderedLong(Object var1, long var2, long var4);
3 其他功能
僅瞭解即可,用到或見到時候便於查看。
3.1 非堆內存操作
//獲得本地指針
public native long getAddress(long address);
//存儲本地指針到給定的內存地址
public native void putAddress(long address, long x);
//分配內存
public native long allocateMemory(long bytes);
//重新分配內存
public native long reallocateMemory(long address, long bytes);
//初始化內存內容
public native void setMemory(Object o, long offset, long bytes, byte value);
public void setMemory(long address, long bytes, byte value) {
setMemory(null, address, bytes, value);
}
//內存內容拷貝
public native void copyMemory(Object srcBase, long srcOffset,Object destBase, long destOffset,long bytes);
public void copyMemory(long srcAddress, long destAddress, long bytes) {
copyMemory(null, srcAddress, null, destAddress, bytes);
}
//釋放內存
public native void freeMemory(long address)
//返回指針的大小,返回值爲4或8
public native int addressSize();
//內存頁的大小
public native int pageSize();
3.2 偏移量操作
//靜態屬性存儲分配中的位置(偏移地址)。
public native long staticFieldOffset(Field arg0);
//非靜態屬性存儲分配中的位置(偏移地址)
public native long objectFieldOffset(Field arg0);
//返回給定的靜態屬性的位置,配合staticFieldOffset方法使用。返回值是靜態屬性所在的Class對象的一個內存快照
//例:unsafe.getObject(unsafe.staticFieldBase(name), unsafe.staticFieldOffset(name))
public native Object staticFieldBase(Field arg0);
//返回數組中第一個元素實際地址相對整個數組對象的地址的偏移量
public native int arrayBaseOffset(Class<?> var1);
//返回數組中第一個元素所佔用的內存空間
public native int arrayIndexScale(Class<?> var1);
3.3 對象操作
類似2.2volatile數據操作,僅是沒有volatile特性,同樣有18個方法,僅展示int類型的:
//獲得給定地址上的int值
public native int getInt(long address);
//設置給定地址上的int值
public native void putInt(long address, int x);
3.4 線程控制
//取消阻塞線程
public native void unpark(Object thread);
//阻塞線程
public native void park(boolean isAbsolute, long time);
3.5 Class操作
//檢測給定的類是否需要初始化。使用在獲取一個類的靜態屬性的時候(因爲一個類如果沒初始化,它的靜態屬性也不會初始化)
public native boolean shouldBeInitialized(Class<?> arg0);
//檢測給定的類是否已經初始化,使用場景同上
public native void ensureClassInitialized(Class<?> arg0);
//定義一個類,返回類實例,此方法會跳過JVM的所有安全檢查。
public native Class<?> defineClass(String name, byte[] b, int off, int len,ClassLoader loader,ProtectionDomain protectionDomain);
//定義一個匿名類,可用於動態創建類
public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches);
//創建一個類的實例,但不會調用這個實例的構造方法,如果這個類還未被初始化,則初始化這個類
public native Object allocateInstance(Class<?> var1) throws InstantiationException;
4 實現自定義原子類
直接看代碼:
public class CASTest {
public static CountDownLatch latch;
public static void main(String[] args) throws Exception {
CASInteger CASInteger = new CASInteger(0);
Runnable casAdd = () -> {
//每個線程修改1萬次
for(int i = 0; i < 10000; i++){
CASInteger.casIncreament();
}
latch.countDown();
};
//線程數量100個
int threadNum = 100;
latch = new CountDownLatch(threadNum);
long start = System.currentTimeMillis();
//啓動所有線程
for(int i = 0; i < threadNum; i++){
new Thread(casAdd).start();
}
//等待所有線程結束,
latch.await();
System.out.println("time cost : " + (System.currentTimeMillis() - start)+" ms");
System.out.println("result: " + CASInteger.getValue());
}
}
/**
* 自定義CAS-Integer,Thread Safe
*/
class CASInteger {
private sun.misc.Unsafe unsafe;
private long offset;
private volatile int value ;
public CASInteger(int value) throws Exception {
this.value = value;
//獲取Unsafe對象
unsafe = getUnsafeByContructor();
//當前對象的偏移量
offset = unsafe.objectFieldOffset(CASInteger.class.getDeclaredField("value"));
}
/**
* 安全自增,僅當更新值比當前對象值多1時,才能更新成功。
*/
public void casIncreament(){
//效果等價於:unsafe.getAndAddInt(this, offset, 1);
while (!unsafe.compareAndSwapInt(this, offset, value, value + 1)){
}
}
public int getValue() {
return value;
}
/**
* 通過私有構造函數Unsafe()獲取Unsafe
*
* @return
* @throws Exception
*/
private static Unsafe getUnsafeByContructor() throws Exception {
// 獲取私有無參構造函數Unsafe()
Constructor<Unsafe> constructor = Unsafe.class.getDeclaredConstructor();
// 關閉安全檢查,不代表一定可以訪問,但一定可以減小安全檢查的資源消耗,常用於反射類功能中
constructor.setAccessible(true);
Unsafe unsafe = (Unsafe) constructor.newInstance();
return unsafe;
}
}
結果:146毫秒,自增100萬次,數據正確
time cost : 113 ms
result: 1000000
PS:value類型是volatile的int類型,爲了保證數據可見;compareAndSwapInt傳參必須是int,不能是Integer,不然可能會內存奔潰哦。
愛家人,愛生活,愛設計,愛編程,擁抱精彩人生!