一:Unsafe概念了解
Unsafe类来自sun.misc包,不属于Java标准。源码类使用了大量的public static final和native方法
Java无法直接访问底层操作系统,而是通过本地(native)方法来访问。不过尽管如此,JVM还是开了一个后门,JDK中有一个类Unsafe,它提供了硬件级别的原子操作。
不能直接进行类的实例化,但是jdk源码之间可以互相实例,unsafe的实例化需要源码授信。
Unsafe在一些开源架构,中间件,系统和程序底层封装大量使用,对Java语言底层操作能力性能方面起了很大的提升。
二:unsafe类的作用
获取某个属性在内存中的位置,比如说修改对象的字段值,即使它是私有的。
获取内存地址偏移量
获取元素的偏移地址,增量地址
还能分配内存,扩充内存,释放内存
示例:
public native long staticFieldOffset(Field paramField);// 内存地址偏移量
public native int arrayBaseOffset(Class paramClass);//偏移地址
public native int arrayIndexScale(Class paramClass);//增量地址
public native long allocateMemory(long paramLong);//分配内存
public native long reallocateMemory(long paramLong1, long paramLong2);//扩充内存
public native void freeMemory(long paramLong);//释放内存
AtomicInteger 等Atomic原子操作底层还是使用unsafe 完成。
源码类
private static final Unsafe theUnsafe;
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException(“Unsafe”);
} else {
return theUnsafe;
}
}
其实VM类方法调用
public static boolean isSystemDomainLoader(ClassLoader var0) {
return var0 == null;
}
1、通过Unsafe类可以分配内存,可以释放内存;
类中提供的3个本地方法allocateMemory、reallocateMemory、freeMemory分别用于分配内存,扩充内存和释放内存,与C语言中的3个方法对应。
public native long allocateMemory(long l);
public native long reallocateMemory(long l, long l1);
public native void freeMemory(long l);
2、可以定位对象某字段的内存位置,也可以修改对象的字段值,即使它是私有的;
JAVA中对象的字段的定位可能通过staticFieldOffset方法实现,该方法返回给定field的内存地址偏移量,这个值对于给定的filed是唯一的且是固定不变的。
public native long staticFieldOffset(Field field);
3、挂起与恢复
将一个线程进行挂起是通过park方法实现的,调用 park后,线程将一直阻塞直到超时或者中断等条件出现。unpark可以终止一个挂起的线程,使其恢复正常。整个并发框架中对线程的挂起操作被封装在 LockSupport类中,LockSupport类中有各种版本pack方法,但最终都调用了Unsafe.park()方法。
public native void unpark(Object var1);
public native void park(boolean var1, long var2);
4、CAS操作 compareAndSwap**** 原子操作
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
5 细节源码
//下面是sun.misc.Unsafe.java类源码 package sun.misc;
import java.lang.reflect.Field;
public class Unsafe
{
// Singleton class.
private static Unsafe unsafe = new Unsafe();
- 使用私有默认构造器防止创建多个实例
private Unsafe(){}
- 获取
Unsafe
的单例,这个方法调用应该防止在不可信的代码中实例, - 因为unsafe类提供了一个低级别的操作,例如直接内存存取。
如果安全管理器不存在或者禁止访问系统属性
**public static Unsafe getUnsafe()
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPropertiesAccess();
return unsafe;
}**
- 返回指定静态field的内存地址偏移量,在这个类的其他方法中这个值只是被用作一个访问
- 特定field的一个方式。这个值对于 给定的field是唯一的,并且后续对该方法的调用都应该
- 返回相同的值。
需要返回偏移量的field
指定field的偏移量
public native long objectFieldOffset(Field field);
- 在obj的offset位置比较integer field和期望的值,如果相同则更新。这个方法
- 的操作应该是原子的,因此提供了一种不可中断的方式更新integer field。
包含要修改field的对象
<code>obj</code>中整型field的偏移量
希望field中存在的值
如果期望值expect与field的当前值相同,设置filed的值为这个新值
如果field的值被更改
public native boolean compareAndSwapInt(Object obj, long offset, int expect, int update);
- 在obj的offset位置比较long field和期望的值,如果相同则更新。这个方法
- 的操作应该是原子的,因此提供了一种不可中断的方式更新long field。
包含要修改field的对象
<code>obj</code>中long型field的偏移量
希望field中存在的值
如果期望值expect与field的当前值相同,设置filed的值为这个新值
如果field的值被更改
public native boolean compareAndSwapLong(Object obj, long offset, long expect, long update);
- 在obj的offset位置比较object field和期望的值,如果相同则更新。这个方法
- 的操作应该是原子的,因此提供了一种不可中断的方式更新object field。
- 包含要修改field的对象
.
<code>obj</code>中object型field的偏移量
- @param expect the expected value of the field.
希望field中存在的值
如果期望值expect与field的当前值相同,设置filed的值为这个新值
如果field的值被更改
public native boolean compareAndSwapObject(Object obj, long offset, Object expect, Object update);
- 设置obj对象中offset偏移地址对应的整型field的值为指定值。这是一个有序或者
- 有延迟的
putIntVolatile方法,并且不保证值的改变被其他线程立
- 即看到。只有在field被
volatile
修饰并且期望被意外修改的时候 - 使用才有用。
- 包含需要修改field的对象
<code>obj</code>中整型field的偏移量
field将被设置的新值
public native void putOrderedInt(Object obj, long offset, int value);
- 设置obj对象中offset偏移地址对应的long型field的值为指定值。这是一个有序或者
- 有延迟的
putLongVolatile方法,并且不保证值的改变被其他线程立
- 即看到。只有在field被
volatile
修饰并且期望被意外修改的时候 - 使用才有用。
- 包含需要修改field的对象
<code>obj</code>中long型field的偏移量
field将被设置的新值
public native void putOrderedLong(Object obj, long offset, long value);
- 设置obj对象中offset偏移地址对应的object型field的值为指定值。这是一个有序或者
- 有延迟的
putObjectVolatile方法,并且不保证值的改变被其他线程立
- 即看到。只有在field被
volatile
修饰并且期望被意外修改的时候 - 使用才有用。
- 包含需要修改field的对象
<code>obj</code>中long型field的偏移量
field将被设置的新值
public native void putOrderedObject(Object obj, long offset, Object value);
- 设置obj对象中offset偏移地址对应的整型field的值为指定值。支持volatile store语义
- 包含需要修改field的对象
<code>obj</code>中整型field的偏移量
field将被设置的新值
public native void putIntVolatile(Object obj, long offset, int value);
- 获取obj对象中offset偏移地址对应的整型field的值,支持volatile load语义。
- 包含需要去读取的field的对象
<code>obj</code>中整型field的偏移量
public native int getIntVolatile(Object obj, long offset);
- 设置obj对象中offset偏移地址对应的long型field的值为指定值。支持volatile store语义
包含需要修改field的对象
code>obj中long型field的偏移量
field将被设置的新值
public native void putLongVolatile(Object obj, long offset, long value);
设置obj对象中offset偏移地址对应的long型field的值为指定值。
包含需要修改field的对象obj
中long型field的偏移量
field将被设置的新值
public native void putLong(Object obj, long offset, long value);
获取obj对象中offset偏移地址对应的long型field的值,支持volatile load语义。
包含需要去读取的field的对象obj
中long型field的偏移量
public native long getLongVolatile(Object obj, long offset);
获取obj对象中offset偏移地址对应的long型field的值
包含需要去读取的field的对象obj
中long型field的偏移量
public native long getLong(Object obj, long offset);
设置obj对象中offset偏移地址对应的object型field的值为指定值。支持volatile store语义
包含需要修改field的对象obj
中object型field的偏移量
field将被设置的新值
public native void putObjectVolatile(Object obj, long offset, Object value);
设置obj对象中offset偏移地址对应的object型field的值为指定值。
包含需要修改field的对象obj
中object型field的偏移量
field将被设置的新值
public native void putObject(Object obj, long offset, Object value);
获取obj对象中offset偏移地址对应的object型field的值,支持volatile load语义。
包含需要去读取的field的对象obj
中object型field的偏移量
public native Object getObjectVolatile(Object obj, long offset);
获取给定数组中第一个元素的偏移地址。
为了存取数组中的元素,这个偏移地址与arrayIndexScale
方法的非0返回值一起被使用。
第一个元素地址被获取的class
数组第一个元素 的偏移地址
public native int arrayBaseOffset(Class arrayClass);
获取用户给定数组寻址的换算因子.一个合适的换算因子不能返回的时候(例如:基本类型),
返回0.这个返回值能够与arrayBaseOffset
一起使用去存取这个数组class中的元素
public native int arrayIndexScale(Class arrayClass);
释放被<a href="#park"><code>park</code></a>创建的在一个线程上的阻塞.这个
方法也可以被使用来终止一个先前调用<code>park</code>导致的阻塞.
这个操作操作时不安全的,因此线程必须保证是活的.这是java代码不是native代码。
要解除阻塞的线程
public native void unpark(Thread thread);
阻塞一个线程直到<a href="#unpark"><code>unpark</code></a>出现、线程
被中断或者timeout时间到期。如果一个<code>unpark</code>调用已经出现了,
这里只计数。timeout为0表示永不过期.当<code>isAbsolute</code>为true时,
timeout是相对于新纪元之后的毫秒。否则这个值就是超时前的纳秒数。这个方法执行时
也可能不合理地返回(没有具体原因)
如果为true timeout的值是一个相对于新纪元之后的毫秒数
可以是一个要等待的纳秒数,或者是一个相对于新纪元之后的毫秒数直到
到达这个时间点
public native void park(boolean isAbsolute, long time);
}