5.jdk底层工具类Unsafe

JDK中的底层Unsafe

unsafe类,是JDK提供的一个工具类unsafe里面的方法大多是native方法,你可以理解为unsafe类是JDK给你提供的一个直接调用操作系统底层功能的一个工具类(直接跟操作系统打交道的一个工具类),unsafe提供了非常多操作系统级别的方法,(不建议程序员直接调用Unsafe类)

(1)比如说通过unsafe可以让操作系统直接给你分配内存、释放内存。
(2)突破java语法本身的限制,直接从内存级别去操作堆里面的某个对象的数据;
(3)调用操作系统的CAS指令,实现CAS的功能
(4)操作系统层次将线程挂起和恢复
(5)提供操作系统级别的内存屏障(之前说过的Load屏障和Store屏障),读取数据强制走主存,修改数据直接刷新到主存

unsafe直接分配和释放内存

下面看一下unsafe提供的直接分配、释放内存,操作内存的一下方法

// 分配bytes大小的堆外内存
public native long allocateMemory(long bytes);
// 还可以执行从address处开始分配,分配bytes大小的堆外内存
public native long reallocateMemory(long address, long bytes);
// 释放allocateMemory和reallocateMemory申请的内存块
public native void freeMemory(long address);
// 将指定对象的给定offset偏移量内存块中的所有字节设置为固定值
// 相当于直接让你在内存级别直接给这个对象的变量赋值了
public native void setMemory(Object o, long offset, long bytes, byte value);

// 设置给定内存地址的long值
// 相当于直接在内存级别给address后面的8个字节赋值
public native void putLong(long address, long x);
// 获取指定内存地址的long值
// 相当于获取address后面的8个字节的值,然后转化为十进制的long值给你
public native long getLong(long address);
// 设置或获取指定内存的byte值
// 相当于获取address后面一个字节的数据,转化成十进制返回给你
public native byte  getByte(long address);
// 直接在内存级别给adderess地址后面的1个字节设置
public native void  putByte(long address, byte x);

这里说的通过操作系统级别分配内存,释放内存,其实就是调用操作系统底层的api去申请和释放内存。

unsafe提供的CAS操作

假如目前有一个Test类是这样子的:

public class Test {
    private DemoClass demo;
    private int intValue;
    private long longValue;
}

有一个Test类的对象 Test o = new Test();

这个时候想要突破java语法的限制,直接修改对象o的private修饰的demo属性。可以通过CAS操作直接去修改对象o里面的demo属性,使用unsafe提供的下面方法:

(1)o就是你要操作的对象

(2)offset就是demo属性在对象o内部的位置,或者偏移量

(3)expected就是demo期待的值

(4)x就是你希望设置的新值,只有demo的值 == expect的时候,才能将demo的值设置成x

public final native boolean compareAndSwapObject( Object o,
                                 long offset,
                                    Object expected,
                                    Object x);

执行CAS操作大致是这样的,根据 对象o的地址demo属性相对于o的偏移量offset,直接计算得到demo所在内存的位置,然后直接将demo的值从内存取出进行CAS(比较替换操作):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GEIhPEgI-1684657159660)(E:/images/640.png)]

同理对于,执行CAS操作替换Test类对象o内部的int值和long值,unsafe提供了如下两个方法:

public final native boolean compareAndSwapInt(Object o,
                                 long offset,
                                 int expected,
                                 int x);

public final native boolean compareAndSwapLong( Object o,
                                  long offset,
                                  long expected,
                                   long x);

底层执行CAS替换的原理跟上面画图讲的demo其实是一样的,这里就不再赘述了

JDK中的CAS是怎么来保证原子性的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sPncjSR7-1684657159661)(E:\images\640.png)]

(1)首先CPU0要执行CAS操作对变量 i 进行赋值,然后CPU0告诉总线说我要申请单独操作变量 i 的权限,帮我告诉一下CPU1等其它的CPU兄弟

(2)总线通知到了CPU1CPU1告诉总线,好的,我不会操作数据,让CPU0大胆的去操作吧

(3)然后总线告诉CPU0,你可以独占变量i的操作了,其它的兄弟表示不会干扰你

(4)然后CPU0从自己的缓存读取 变量 i 的值;然后又根据 (o对象地址 + offset偏移量地址) 直接定位到变量 i内存的位置,直接读取变量i在内存的值

(5)接下来的操作就简单了,由于不会有人干扰,直接对比缓存的值和内存的值是否一致就可以了,如果一致,我直接修改,然后刷回主内存;如果不一致,说明我本地的数据不是最新的,需要重新申请CAS操作。

unsafe将线程挂起和恢复

unsafe类提供类将一个线程挂起、将一个挂起的线程唤醒的方法,分别是parkunpark,我们看如下的代码:

park方法

//线程调用该方法,线程将一直阻塞直到被唤醒,或者超时,//或者中断条件出现。
public native void park(boolean isAbsolute, long time);

(1) isAbsolute是否是绝对时间,当isAbsolute == true ,后面time的时间单位是ms;当为false的时候,后面time参数的时间单位是ns。

(2)time > 0时候,表示大概要将线程挂起time的时间,过了时间后自动将线程唤醒。当time = 0的时候,表示一直将线程挂起,直到有人调用unpark方法将线程唤醒。

unpark方法

public native void unpark(Object thread);

直接将正在被挂起的thread线程唤醒,让它继续干活

LockSupport

LockSupport是对unsafe中parkupark功能封装的一个工具类,提供了阻塞和唤醒功能。

我们可以直接使用LockSupport的方法达到挂起和恢复线程的效果,LockSupport方法的源码如下:

public class LockSupport {
    public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        // 这里直接调用unsafe的park方法将线程挂起
        UNSAFE.park(false, 0L);
        setBlocker(t, null);
    }
    
    public static void unpark(Thread thread) {
        if (thread != null)
            // 直接调用unsafe的unpark方法将线程唤醒
            UNSAFE.unpark(thread);
    }
}

由于我们自己编写的java程序不能直接使用unsafe工具类,所以啊JDK还是有一些工具类对unsafe类的功能进行封装,然后我们就直接使用这些封装的工具类即可。

内存屏障

unsafe提供了几种内存屏障:

// 在该方法之前的所有读操作,一定在load屏障之前执行完成
public native void loadFence();
// 在该方法之前的所有写操作,一定在store屏障之前执行完成
public native void storeFence();
// 在该方法之前的所有读写操作,一定在full屏障之前执行完成,这个内存屏障相当于上面两个的合体功能
public native void fullFence();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值