自学内容网 自学内容网

J.U.C - 基础工具类:Unsafe类


在这里插入图片描述


Unsafe 类简介

Java不能像C/C++一样直接操作内存区域,需要通过本地方法的方式来操作内存区域。JDK可以通过一个“后门”——Unsafe类,执行底层硬件级别的CAS原子操作,线程阻塞和唤醒等。

Unsafe 类是 Java 中一个非常特殊的类,它提供了硬件级别的原子操作和直接内存访问等功能。

Unsafe类中的方法几乎全部都是native方法,它们使用JNI的方式调用本地的C++类库。由于其强大的功能和潜在的风险,Unsafe 类并未公开暴露给普通开发者,而是被隐藏在 sun.misc 包中。


主要功能

  1. 内存操作

    • allocateMemory(long bytes): 分配指定大小的内存。
    • freeMemory(long address): 释放指定地址的内存。
    • getLong(long address): 从指定地址读取长整型值。
    • putLong(long address, long value): 将长整型值写入指定地址。
  2. 对象操作

    • allocateInstance(Class<T> cls): 分配一个新的对象实例,但不调用构造函数。
    • copyMemory(Object src, long srcOffset, Object dest, long destOffset, long bytes): 复制内存。
    • objectFieldOffset(Field field): 获取字段在对象中的偏移量。
  3. 数组操作

    • arrayBaseOffset(Class<?> arrayClass): 获取数组的基地址偏移量。
    • arrayIndexScale(Class<?> arrayClass): 获取数组元素的大小。
  4. 线程操作

    • park(boolean isAbsolute, long time): 挂起当前线程。
    • unpark(Thread thread): 唤醒指定线程。
  5. CAS 操作

    • compareAndSwapInt(Object o, long offset, int expectedValue, int newValue): CAS 操作,用于原子地更新整型值。
    • compareAndSwapLong(Object o, long offset, long expectedValue, long newValue): CAS 操作,用于原子地更新长整型值。
    • compareAndSwapObject(Object o, long offset, Object expectedValue, Object newValue): CAS 操作,用于原子地更新引用值。

CAS操作

CAS是一种实现并发算法时常用的技术,自旋锁和乐观锁的实现都用到了CAS算法。JUC并发包的绝大多数工具类,如原子类AtomicInteger和重入锁ReentrantLock,它们的源码实现中都有CAS的身影。

CAS是Compare And Swap的简称,即比较再替换。它是计算机处理器提供的一个原子指令,保证了比较和替换两个操作的原子性。

CAS操作涉及三个操作数:CAS(V,E,N)​。

  • 1)V:要读写的内存地址;
  • 2)E:进行比较的值E(预期值)​;
  • 3)N:拟写入的新值。

CAS操作含义:当且仅当内存地址V中的值等于预期值E时,将内存V中的值改为A,否则不操作。

注意:CAS(V,E,N)是一个伪函数,这么写是为了更好的理解CAS的含义。

假设存在多个线程执行CAS操作并且CAS的步骤很多,有没有可能在判断V和E相同后,正要赋值时,切换了线程,更改了值。造成了数据不一致呢?答案是否定的。因为CAS是一条CPU的原子指令,在执行过程中不允许被中断,所以不会造成所谓的数据不一致问题


CAS的三个问题

ABA问题

如果一个变量V初次读取的时候是A值,那在赋值的时候检查到它仍然是A值,是否就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个问题被称为CAS操作的"ABA"问题。

ABA问题的产生是因为变量值产生了“环形”更改,即一个变量的值从A改为B,随后又从B改回A。如果变量的值只能朝一个方向转换,就不会构成“环形”​,比如使用版本号或者时间戳机制,版本号机制是每次更改版本号变量时将版本号递增加1,这样就不会存在这个问题了。

JDK中的AtomicStampedReference类使用的就是时间戳机制,它给每个变量都配备了一个时间戳,来避免ABA问题的产生。


循环时间长开销大

自旋CAS(也就是更新不成功就一直循环执行直到成功)如果长时间不成功,会给CPU带来非常大的执行开销。遇到这种情况,就需要对CAS操作限制重试上限,如果重试次数达到最大值,可以通过直接退出或者采用其他方式来替代CAS。

比如synchronized同步锁,轻量级锁通过CAS自旋等待锁释放,在线程竞争激烈的情况下,自旋次数达到一定数量时,synchronized内部会升级为重量级锁。


只能保证一个共享变量的原子操作

CAS操作只对单个共享变量有效,当操作跨多个共享变量时CAS无效


示例

内存操作

import sun.misc.Unsafe;
import java.lang.reflect.Field;

public class UnsafeExample {
    private static final Unsafe unsafe;

    static {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        // 示例:分配内存
        long address = unsafe.allocateMemory(8);
        System.out.println("Allocated memory at address: " + address);

        // 示例:写入和读取内存
        unsafe.putLong(address, 123456789L);
        long value = unsafe.getLong(address);
        System.out.println("Value read from memory: " + value);

        // 示例:释放内存
        unsafe.freeMemory(address);
    }
}

compareAndSwapInt

import sun.misc.Unsafe;
import java.lang.reflect.Field;

public class UnsafeCASExample {
    private static final Unsafe unsafe;
    private static final long valueOffset;

    static {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);

            valueOffset = unsafe.objectFieldOffset(
                UnsafeCASExample.class.getDeclaredField("value")
            );
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private volatile int value;

    public boolean cas(int expectedValue, int newValue) {
        return unsafe.compareAndSwapInt(this, valueOffset, expectedValue, newValue);
    }

    public static void main(String[] args) {
        UnsafeCASExample example = new UnsafeCASExample();
        example.value = 10;

        boolean result = example.cas(10, 20);
        System.out.println("CAS result: " + result); // 输出: CAS result: true
        System.out.println("New value: " + example.value); // 输出: New value: 20

        result = example.cas(10, 30);
        System.out.println("CAS result: " + result); // 输出: CAS result: false
        System.out.println("Value remains: " + example.value); // 输出: Value remains: 20
    }
}

compareAndSwapLong

import sun.misc.Unsafe;
import java.lang.reflect.Field;

public class UnsafeCASLongExample {
    private static final Unsafe unsafe;
    private static final long valueOffset;

    static {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);

            valueOffset = unsafe.objectFieldOffset(
                UnsafeCASLongExample.class.getDeclaredField("value")
            );
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private volatile long value;

    public boolean cas(long expectedValue, long newValue) {
        return unsafe.compareAndSwapLong(this, valueOffset, expectedValue, newValue);
    }

    public static void main(String[] args) {
        UnsafeCASLongExample example = new UnsafeCASLongExample();
        example.value = 100L;

        boolean result = example.cas(100L, 200L);
        System.out.println("CAS result: " + result); // 输出: CAS result: true
        System.out.println("New value: " + example.value); // 输出: New value: 200

        result = example.cas(100L, 300L);
        System.out.println("CAS result: " + result); // 输出: CAS result: false
        System.out.println("Value remains: " + example.value); // 输出: Value remains: 200
    }
}

compareAndSwapObject

import sun.misc.Unsafe;
import java.lang.reflect.Field;

public class UnsafeCASObjectExample {
    private static final Unsafe unsafe;
    private static final long valueOffset;

    static {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);

            valueOffset = unsafe.objectFieldOffset(
                UnsafeCASObjectExample.class.getDeclaredField("value")
            );
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private volatile String value;

    public boolean cas(String expectedValue, String newValue) {
        return unsafe.compareAndSwapObject(this, valueOffset, expectedValue, newValue);
    }

    public static void main(String[] args) {
        UnsafeCASObjectExample example = new UnsafeCASObjectExample();
        example.value = "Hello";

        boolean result = example.cas("Hello", "World");
        System.out.println("CAS result: " + result); // 输出: CAS result: true
        System.out.println("New value: " + example.value); // 输出: New value: World

        result = example.cas("Hello", "Java");
        System.out.println("CAS result: " + result); // 输出: CAS result: false
        System.out.println("Value remains: " + example.value); // 输出: Value remains: World
    }
}

在多线程环境中非常有用,可以确保操作的原子性和线程安全性


注意事项

  1. 安全性Unsafe 类的操作绕过了 JVM 的安全检查,不当使用可能导致内存泄漏、数据损坏等问题。
  2. 兼容性Unsafe 类是 JDK 内部实现的一部分,不同版本的 JDK 可能会有不同的行为,甚至在未来版本中可能会被移除。
  3. 替代方案:对于大多数应用场景,推荐使用 java.util.concurrent 包中的工具类,如 AtomicIntegerAtomicLong 等,它们提供了更安全、更稳定的并发操作。

在这里插入图片描述


原文地址:https://blog.csdn.net/yangshangwei/article/details/143749935

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!