当前位置: 首页 > article >正文

Unsafe

1. 概念介绍

sun.misc.Unsafe 是 Java 中的一个特殊类,它提供了一组低级别的、不安全的操作,这些操作通常是 JVM 内部使用的。由于这些操作非常强大且危险,因此 Unsafe 类被设计为只能在受信任的代码中使用。

2. 主要功能和用途

  1. 内存操作

    • 直接内存访问Unsafe 允许直接操作内存地址,绕过 Java 的内存管理机制。例如,allocateMemoryfreeMemory 方法可以分配和释放内存。
    • 数组操作arrayBaseOffsetarrayIndexScale 方法可以获取数组的基地址和元素大小,从而实现对数组的直接操作。
  2. 对象操作

    • 对象创建allocateInstance 方法可以直接创建对象实例,而不调用构造函数。
    • 字段访问objectFieldOffset 方法可以获取对象字段的偏移量,从而实现对字段的直接读写。
  3. 线程操作

    • 线程挂起和恢复parkunpark 方法可以挂起和恢复线程。
  4. CAS(Compare-And-Swap)操作

    • 原子操作compareAndSwapIntcompareAndSwapLong 等方法提供了原子性的比较和交换操作,这是实现无锁数据结构的基础。
  5. 内存屏障

    • 内存屏障loadFencestoreFencefullFence 方法可以插入内存屏障,控制内存操作的顺序。

示例代码

以下是一些 Unsafe 类的常见用法示例:

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

public class UnsafeExample {
    public static void main(String[] args) throws Exception {
        // 获取 Unsafe 实例
        Unsafe unsafe = getUnsafe();

        // 直接分配内存
        long address = unsafe.allocateMemory(1024);
        System.out.println("Allocated memory at address: " + address);

        // 写入和读取内存
        unsafe.putByte(address, (byte) 42);
        byte value = unsafe.getByte(address);
        System.out.println("Read byte from memory: " + value);

        // 释放内存
        unsafe.freeMemory(address);

        // 创建对象实例而不调用构造函数
        ExampleClass obj = (ExampleClass) unsafe.allocateInstance(ExampleClass.class);
        System.out.println("Created instance without calling constructor: " + obj);

        // CAS 操作
        long offset = unsafe.objectFieldOffset(ExampleClass.class.getDeclaredField("value"));
        boolean success = unsafe.compareAndSwapInt(obj, offset, 0, 42);
        System.out.println("CAS operation result: " + success);
        System.out.println("Updated value: " + obj.getValue());
    }

    private static Unsafe getUnsafe() throws Exception {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        return (Unsafe) field.get(null);
    }

    static class ExampleClass {
        private int value;

        public ExampleClass() {
            this.value = 10;
        }

        public int getValue() {
            return value;
        }
    }
}

其中运行结果如下:
在这里插入图片描述

	private static Unsafe getUnsafe() throws Exception {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        return (Unsafe) field.get(null);
    }

其中theUnsafe是unsafe中的属性,目的是为了获取unsage对象来操作。

源码如下

package sun.misc;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;

public final class Unsafe {
    private static final Unsafe theUnsafe;
    public static final int INVALID_FIELD_OFFSET = -1;
    public static final int ARRAY_BOOLEAN_BASE_OFFSET;
    ```

3. 注意事项

  1. 安全性Unsafe 类提供了非常底层的操作,使用不当可能会导致 JVM 崩溃、内存泄漏或其他未定义行为。因此,它通常只应在受信任的代码中使用。
  2. 兼容性Unsafe 类是 sun.misc 包的一部分,这意味着它不是 Java 标准库的一部分,可能在未来的 Java 版本中被移除或更改。
  3. 性能:虽然 Unsafe 类提供了高性能的操作,但它们通常也更复杂且难以调试。

总结

sun.misc.Unsafe 类提供了许多强大的底层操作,适用于需要高性能或特殊操作的场景。然而,由于其潜在的危险性和非标准性,使用时应格外小心。

4. 举例说明: AtomicBoolean

4.1 源码如下
public class AtomicBoolean implements java.io.Serializable {
    private static final long serialVersionUID = 4654671469794556979L;
    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicBoolean.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;
    ...
    public final boolean compareAndSet(boolean expect, boolean update) {
        int e = expect ? 1 : 0;
        int u = update ? 1 : 0;
        return unsafe.compareAndSwapInt(this, valueOffset, e, u);
    }
    /**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     *
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public boolean weakCompareAndSet(boolean expect, boolean update {
        int e = expect ? 1 : 0;
        int u = update ? 1 : 0;
        return unsafe.compareAndSwapInt(this, valueOffset, e, u);
    }
    
    /**
     * Unconditionally sets to the given value.
     *
     * @param newValue the new value
     */
    public final void set(boolean newValue) {
        value = newValue ? 1 : 0;
    }
    
    /**
     * Eventually sets to the given value.
     *
     * @param newValue the new value
     * @since 1.6
     */
    public final void lazySet(boolean newValue) {
        int v = newValue ? 1 : 0;
        unsafe.putOrderedInt(this, valueOffset, v);
    }

    /**
     * Atomically sets to the given value and returns the previous value.
     *
     * @param newValue the new value
     * @return the previous value
     */
    public final boolean getAndSet(boolean newValue) {
        boolean prev;
        do {
            prev = get();
        } while (!compareAndSet(prev, newValue));
        return prev;
    }
    

方法中getAndSet 是原子操作,来设置值。
说实话weakCompareAndSetcompareAndSet两个方法没看出来有什么区别。
为了保证原子性,value还是使用volatile 修饰。

volatile 是 Java 中的一个关键字,用于修饰变量。它的主要作用是确保变量的可见性和禁止指令重排序。以下是 volatile 的详细作用和使用场景:

  1. 可见性(Visibility)
    当一个变量被声明为 volatile 时,所有线程都能看到该变量的最新值。具体来说:
  • 写操作:当一个线程对 volatile 变量进行写操作时,该操作会立即刷新到主内存中。
  • 读操作:当一个线程对 volatile 变量进行读操作时,该操作会从主内存中读取最新的值,而不是从线程的本地缓存(如 CPU 缓存)中读取。
  1. 禁止指令重排序(Preventing Instruction Reordering)
    volatile 变量的读写操作会被编译器和处理器特殊对待,以确保它们不会被重排序。具体来说:
  • 写操作:在写 volatile 变量之前,所有之前的写操作都会被刷新到主内存中。
  • 读操作:在读 volatile 变量之后,所有之后的读操作都会从主内存中读取最新的值。
4.2 使用场景

volatile 通常用于以下场景:

  1. 状态标志:
    当一个变量用于表示某种状态标志(如 boolean 类型的标志)时,可以使用 volatile 来确保所有线程都能看到最新的状态。
    例如,一个线程可以设置一个 volatile 的 boolean 变量来通知其他线程某个操作已经完成。
  2. 单例模式的双重检查锁定(Double-Checked Locking):
    在单例模式的实现中,可以使用 volatile 来确保实例的初始化操作不会被重排序,从而避免潜在的线程安全问题。
4.3 注意事项
  • 原子性
    • volatile 只能保证变量的可见性和禁止指令重排序,但不能保证复合操作的原子性。例如,volatile 不能替代 synchronized 或 AtomicInteger 等原子类来实现原子操作。
  • 性能:
    • volatile 的性能开销比普通变量要高,因为它涉及到主内存的读写操作。因此,只有在确实需要确保可见性和禁止重排序时才使用 volatile。

5. compareAndSwapInt 底层原理

Unsafe 类的 compareAndSwapInt 方法是一个本地方法(native method),它的底层实现依赖于操作系统和硬件平台。具体来说,它通常利用了处理器提供的原子性指令(如 x86 架构上的 CMPXCHG 指令)来实现比较和交换操作。

5.1 底层实现原理
  1. 处理器指令

    • compareAndSwapInt 方法的核心是利用处理器的原子性指令来确保操作的原子性。在 x86 架构上,这个指令通常是 CMPXCHG
    • CMPXCHG 指令会比较目标内存位置的值与一个期望值,如果相等,则将新值写入目标内存位置;否则,不进行任何操作。
  2. 内存屏障

    • 为了确保操作的原子性和顺序性,compareAndSwapInt 方法通常会插入内存屏障(memory barrier)。内存屏障可以防止指令重排序,确保在屏障之前的所有内存操作都完成之后,屏障之后的内存操作才能开始。
  3. 操作系统支持

    • 不同的操作系统和硬件平台可能有不同的实现方式。例如,在 Linux 上,JVM 可能会调用内核提供的原子操作函数(如 __sync_bool_compare_and_swap)。
5.2 示例代码

以下是一个简化的伪代码,展示了 compareAndSwapInt 方法的底层实现思路:

// 伪代码,实际实现可能更复杂
bool compareAndSwapInt(Object* obj, long offset, int expected, int newValue) {
    // 获取对象的内存地址
    int* address = (int*)((char*)obj + offset);

    // 使用处理器指令进行比较和交换
    bool success = false;
    __asm__ volatile (
        "lock cmpxchg %4, (%3)\n"
        "sete %0\n"
        : "=q" (success)
        : "a" (expected), "q" (newValue), "r" (address)
        : "memory"
    );

    return success;
}
5.3 解释
  1. __asm__ volatile
    • 这是 GCC 内联汇编的语法,用于在 C 代码中嵌入汇编指令。
    • lock cmpxchg %4, (%3):这是 x86 架构上的 CMPXCHG 指令,它会锁定总线,确保操作的原子性。
    • sete %0:如果 CMPXCHG 操作成功(即目标内存位置的值等于期望值),则将 success 设置为 true
  2. 内存屏障
    • __asm__ volatile 中的 "memory" 约束表示这是一个内存屏障,确保在执行这段汇编代码时,之前的所有内存操作都已完成。
5.4 总结

compareAndSwapInt 方法的底层实现依赖于处理器提供的原子性指令(如 CMPXCHG)和内存屏障,以确保操作的原子性和顺序性。不同的操作系统和硬件平台可能有不同的实现方式,但核心思想是利用处理器的原子性指令来实现比较和交换操作。


http://www.kler.cn/a/513015.html

相关文章:

  • 1688 满足跨境业务需求而提供的一组 API 接口
  • 【JavaSE】(8) String 类
  • C++ ——— 模拟实现 vector 类
  • STM32单片机:GPIO模式
  • OpenCV相机标定与3D重建(63)校正图像的畸变函数undistort()的使用
  • C++ 内存分配和管理(八股总结)
  • MySQL指定表使用的存储引擎
  • AI大模型-提示工程学习笔记10-链式提示
  • Web小练习01
  • 将AWS S3设置为类SFTP服务用于数据上传
  • 从零搭建一个Vue3 + Typescript的脚手架——day2
  • Linux——入门基本指令汇总
  • ubuntu22.04编译多个版本OpenCV
  • Linux高并发服务器开发 第十五天(fork函数)
  • 领域算法 - 大数据处理
  • Git 详细安装教程以及gitlab添加SSH密钥
  • 微头条业务流程
  • 实战演示:利用ChatGPT高效撰写论文
  • 【C/C++实现】直接插入排序(图例--超详细解析,小白一看就会!)
  • 【单片机通过蜂鸣器模拟警号 救护车 警车 等声音 】
  • node.js+npm的环境配置以及添加镜像(保姆级教程)
  • [LeetCode] 哈希表 I — 242#有效的字母异位词 | 349#两个数组的交集 | 202#快乐数 | 1#两数之和
  • 【Rust自学】13.10. 性能对比:循环 vs. 迭代器
  • Excel 技巧12 - 如何在Excel中输入对号叉号(★),字体Wingdings2
  • 鸿蒙Harmony json转对象(1)
  • Golang 生态学习