前言
- 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,不然可能会内存奔溃哦。
爱家人,爱生活,爱设计,爱编程,拥抱精彩人生!