之前看JUC的源码,一直有发现一个怪异的类:Unsafe,怪,是因为名字有点怪......它在sun.misc包下,
不属于Java标准,但是很多java的高性能的类都基于Unsafe,而且它竟然可以直接操作内存,让java直
接操作内存是很危险的,这个类就明着告诉我们,不安全,不要直接用我
先看一个最常见到的方法:getUnsafe()
private Unsafe() {} private static final Unsafe theUnsafe = new Unsafe(); @CallerSensitive ------这个注解查了下是用来控制权限的,跟踪到最初的调用者,望文生义:调用者敏感的 public static Unsafe getUnsafe() { Class<?> caller = Reflection.getCallerClass();
//仅在BootstrapClassLoader加载时是合法的 if (!VM.isSystemDomainLoader(caller.getClassLoader())) throw new SecurityException("Unsafe"); return theUnsafe; }
很容易看出来是个单列模式,上面的注解,用来控制权限,可以跟踪到最初的调用者,可以望文生义的理解:
调用者敏感;普通调用者调用直接抛异常,必须是systemDomainLoader这样的classLoader(BootstrapClassLoader)
Unsafe类提供的API的脑图:
/**
* @param o 包含要修改field的对象
* @param offset 对象中某field的偏移量
* @param expected 期望值(预期的原值)
* @param update 更新值
* @return true | false
*/
public native boolean compareAndSwapInt(Object obj, long offset, int expect, int update);
public native boolean compareAndSwapLong(Object obj, long offset, long expect, long update);
public native boolean compareAndSwapObject(Object obj, long offset, Object expect, Object update);
Unsafe提供的CAS操作有这三个,都是compareAndSwapXXX的形式,执行的是一条CPU的原子指令(cmpxchg)
CAS操作有3个操作数,内存值M,预期值E,新值U,如果M==E,则将内存值修改为U,否则啥都不做
这个对比下mysql中的MVCC机制很好理解,MVCC存的是个隐藏的版本号值,做记录变更的操作时会拿
先前已经获取到的版本号跟当前记录最新的版本号做比较(就像比较M和E),然后决定操作是成功还是失败
典型应用:java.util.concurrent.atomic相关类、Java AQS、CurrentHashMap
包含堆外内存的分配、拷贝、释放、给定地址值操作等方法
在Java中创建的对象都处于堆内内存(heap)中,堆内内存是由JVM所管控的Java进程内存,遵循JVM的内存
管理机制,JVM会采用垃圾回收机制统一管理堆内存。
堆外内存存在于JVM管控之外的内存区域,Java中对堆外内存的操作,依赖于Unsafe提供的和memory相关的
native方法,如分配内存allocateMemory,扩充内存reallocateMemory,释放内存freeMemory
包括线程的挂起,恢复,锁机制方法
//取消阻塞线程 public native void unpark(Object thread); //阻塞线程 public native void park(boolean isAbsolute, long time); //获得对象锁(可重入锁) @Deprecated
public native void monitorEnter(Object o); //释放对象锁 @Deprecated
public native void monitorExit(Object o); //尝试获取对象锁 @Deprecated
public native boolean tryMonitorEnter(Object o);
锁机制相关的native方法已过时,不关注
park:将一个线程挂起,调用park方法后,线程将一直阻塞直到超时或者中断等条件出现;
unpark:终止一个挂起的线程,使其恢复正常
典型应用:
Java锁和同步器框架的核心类AbstractQueuedSynchronizer,通过调用LockSupport.park()
和LockSupport.unpark()
实现线程的阻塞和唤醒,LockSupport的park、unpark方法实际是调用Unsafe的park、unpark方式来实现
LockSupport先点名,下一篇上
有点熟悉,volatile也用到了内存屏障,用来禁止代码重排序,Unsafe的这几个方法也是为了禁止重排,理解上是一样的
//内存屏障,禁止load操作重排序。屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前 public native void loadFence(); //内存屏障,禁止store操作重排序。屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前 public native void storeFence(); //内存屏障,禁止load、store操作重排序 public native void fullFence();
典型应用
java8中读写锁的改进版本:StampedLock
StampedLock.validate方法源码:
StampedLock的典型用法是
1、获取乐观读锁
2、copy变量到工作内存
3、锁状态检测
其中第2和3步要保证不会发生重排序,因为按照执行逻辑,2必须在3之前发生,3处使用loadFence()加上内存屏障,保证顺序
StampedLock类,后面介绍
其他功能,碰到的位置比较少,不做介绍
单例模式的体现;
提供了很多native的不安全操作的方法;
提供了三个CAS原子操作的API方法;
提供了JUC下实现"锁"的基本类(Atomic,AQS,LockSupport)所需的线程调度和CAS方法
原文:https://www.cnblogs.com/yb38156/p/14434497.html