MENU

Java - Unsafe类

July 27, 2020 • Read: 1579 • 后端

Unsafe类

Unsafe类简单来说就是不安全的类,因为是直接拿到数据的真实内存地址,直接操作内存,脱离于语言本身。里面的方法基本都是native方法。

1. Java中的CAS操作

使用锁处理并发问题,有一个缺点就是线程没有获取到锁的时候会被阻塞挂起,这会导致线程上下文的切换和重新调度开销。由于volatile不能解决原子性问题,Java中由JDK提供的非阻塞原子性操作,即CAS(Compare and Swap)。它通过硬件保证比较-更新的原子性。

CAS操作有个经典的ABA问题,感兴趣的可以百度看一看。解决方案:可以给每个变量加上时间戳。

2. Unsafe类中的重要方法

如何获取

不能直接通过调用功能getUnsafe方法来得到Unsafe,因为其方法会判断是否使用Bootstrap加载器加载类的。其原因是Unsafe类是直接操作内存,这样很不安全。

// 所以我们只能通过反射来获取
public static Unsafe getUnsafe() throws NoSuchFieldException, IllegalAccessException {
    Field field = Unsafe.class.getDeclaredField("theUnsafe");
    // 设置其为可访问的
    field.setAccessible(true);
    return (Unsafe) field.get(null);
}

objectFieldOffset()方法

// 本地方法
public native long objectFieldOffset(Field var1);
案例
// AQS类使用该方法,获取所属类中的内存偏移地址,提供给Unsafe类其他方法使用,改变其值
static {
    try {
        stateOffset = unsafe.objectFieldOffset
            (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
        ....
    } catch (Exception ex) { throw new Error(ex); }
}

compareAndSwapObject()方法

compareAndSwap的意思是比较并交换,CAS有四个操作数,分别为:对象的内存位置,对象中变量的偏移量,变量预期值,新的值

public final native boolean compareAndSwapObject(Object var1, long valueOffset, Object expect, Object update);
案例
// AQS中修改state变量的值
protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

getLongVolatile()方法

获取对象obj中偏移量为offset的变量对应volatile语义的值

public native long getLongVolatile(Object obj, long offset);

getAndSetLong()方法

该方法是获取当前变量的值,然后使用CAS原子操作设置新值。如果更新失败(多个线程同时更新变量的值时),则重新再次尝试。

public final long getAndSetLong(Object var1, long offset, long var4) {
    long var6;
    do {
        var6 = this.getLongVolatile(var1, offset);
        // 如果设置失败,会循环再次设置,知道成功
    } while(!this.compareAndSwapLong(var1, offset, var6, var4));

    return var6;
}
案例

AtomicLong类,Java中的原子类都是调用Unsafe类的CAS操作来完成院子操作的。同时提醒一个点:单个原子类的操作是具有原子性的,但是多个组合就不具有了

public final long getAndSet(long newValue) {
    return unsafe.getAndSetLong(this, valueOffset, newValue);
}

getAndAddLong()方法

获取对象obj中偏移量为offset的变量volatile语义值,并设置其值=原始值+addValue

public final long getAndAddLong(Object obj, long offset, long addValue) {
    long var6;
    do {
        var6 = this.getLongVolatile(obj, offset);
        // 如果设置失败,会循环再次设置,知道成功
    } while(!this.compareAndSwapLong(obj, offset, var6, var6 + addValue));
    // 返回的还是原始值
    return var6;
}
案例
// 调用Unsafe类的方法,原子性设置value的值加1,并返回value原来的值
public final long getAndIncrement() {
    return unsafe.getAndAddLong(this, valueOffset, 1L);
}

park()、unpark()

park(boolean isAbsolute, long time):阻塞当前线程

unpark(Object thread):唤醒调用park阻塞的线程

LockSupport

他提供了基本的使用方法,类似于Unsafe的直接操作,他可以间接操作。基本上是对Unsafe类的park和unpark方法封装,但增加了一些额外的提示信息(比如:可以在线程堆栈信息查看到更多有关阻塞对象的信息)。

Last Modified: October 8, 2020
Leave a Comment