目录
- 一、Unsafe是什么?
- 二、Unsafe对象的获取
- 三、CAS
- 1、相关方法
- 2、demo
- 四、数组操作
- 五、内存分配
- 六、线程调度
参考于:https://blog.csdn.net/Wisimer/article/details/115220750
一、Unsafe是什么?
Unsafe是jdk提供的一个直接访问操作系统资源的工具类(底层c++实现),它可以直接分配内存,内存复制,copy,提供cpu级别的CAS乐观锁等操作。
Unsafe位于sun.misc包下,jdk中的并发编程包juc(java.util.concurrent)基本全部靠Unsafe实现,由此可见其重要性。
二、Unsafe对象的获取
查看源码:
结论:
- Unsafe是饿汉式的单例模式
- 只允许被引导类加载器(BootstrapClassLoader)加载的类使用,查看源码可以看到在获取unsafe对象时会判断调用类是否是系统类加载器加载的,所以我们无法在自己的类中直接通过Unsafe.getUnsafe()获取unsafe对象。所以只能通过反射直接new一个或者将其内部静态成员变量theUnsafe获取出来
public static void main(String[] args) throws Exception{
Class<Unsafe> unsafeClass = Unsafe.class;
//方法一:通过反射构造一个Unsafe对象
Constructor<Unsafe> constructor = unsafeClass.getDeclaredConstructor();
constructor.setAccessible(true);
Unsafe unsafe1 = constructor.newInstance();
System.out.println(unsafe1);
//方法二:获取内部静态成员变量
Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe2 = (Unsafe) theUnsafe.get(null);
System.out.println(unsafe2);
}
三、CAS
CAS译为Compare And Swap,它是乐观锁的一种实现。假设主存值为pre,预期值为expect,想要更新成得值为update,当且仅当主存值pre等预期值expect时,才将pre更新为update。
1、相关方法
在unsafe中,实现CAS算法通过cpu的原子指令cmpxchg实现,它对应的方法如下:
简单介绍下它使用的参数:
- 第一个参数 var1为内存中要操作的对象
- 第二个参数 var2为要操作的值的内存地址偏移量
- 第三个参数 var4为预期值
- 第四个参数 var5 为想要更新成的值
为了方便理解,举个栗子。类User有一个成员变量name。我们new了一个对象User后,就知道了它(User对象)在内存中的起始值,而员变量name在对象中的位置偏移是固定的。这样通过这个起始值和这个偏移量就能够定位到成员变量name在内存中的具体位置。
如何得出name在对象User中的偏移量,Unsafe自然也提供了相应的方法:
2、demo
import sun.misc.Unsafe;
import java.lang.reflect.*;
public class UnsafeDemo {
public static void main(String[] args) throws Exception {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
User user = new User("jsbintask");
long nameOffset = unsafe.objectFieldOffset(User.class.getDeclaredField("name"));
boolean res1 = unsafe.compareAndSwapObject(user, nameOffset, "jsbintask1", "jsbintask2");
System.out.println(res1+", 第一次更新后的值:" + user.getName());
boolean res2 = unsafe.compareAndSwapObject(user, nameOffset, "jsbintask", "jsbintask2");
System.out.println(res2+", 第二次更新后的值:" + user.getName());
}
public static class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
}
因为内存中name的值为"jsbintask",而第一次使用compareAndSwapObject方法预期值为"jsbintask1",这显然是不相等的,所以第一次更新失败,返回false。第二次我们传入了正确的预期值,返回true,更新成功!
四、数组操作
之后用到的时候再进行补充
五、内存分配
Unsafe还给我们提供了直接分配内存,释放内存,拷贝内存,内存设置等方法,值得注意的是,这里的内存指的是堆外内存!它是不受jvm内存模型掌控的,所以使用需要及其小心:
之后用到的时候再进行补充
六、线程调度
通过Unsafe还可以直接将某个线程挂起,这和调用Object.wait()方法作用是一样的,但是效率确更高!
我们熟知的AQS(AbstractQueuedSynchronizer)内部挂起线程使用了LockSupport,而LockSupport内部依旧使用的是Unsafe:
之后用到的时候再进行补充