秋招Java后端开发——基础篇4(Unsafe类详解)

本文对Java中的Unsafe类及其常见功能进行总结。

一、介绍

Unsafe类主要用于执行低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等,其实现依赖于本地方法(Native method,用其他语言(如C/C++/汇编)编写的)

二、Unsafe类的使用

  1. Unsafe 类为一单例实现,提供静态方法 getUnsafe 获取 Unsafe实例。但由于其操作非常底层,为了保证安全性,只有通过启动类加载器加载的类才能够调用 Unsafe 类中的方法。
  2. 非启动类加载器加载类使用Unsafe类的方法通过反射
import sun.misc.Unsafe;
import java.lang.reflect.Field;

public class UnsafeUtils {

    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("Unable to get Unsafe instance", e);
        }
    }

    public static Unsafe getUnsafe() {
        return unsafe;
    }
}

三、 常用功能

1. 内存操作(分配、写入、读取)
(1)Unsafe类提供的内存操作方法

  • 内存分配:使用 unsafe.allocateMemory
  • 内存写入:使用 unsafe.putByte 在指定内存地址写入字节数据
  • 内存读取:使用 unsafe.getByte 从指定内存地址读取字节数据
  • 内存释放:使用 unsafe.freeMemory

(2)示例:

import sun.misc.Unsafe;

import java.lang.reflect.Field;

public class UnsafeExample {

    private static final Unsafe unsafe;
    private static final long BYTE_ARRAY_BASE_OFFSET;

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

            // 获取 byte[] 数组中第一个元素的偏移量
            BYTE_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(byte[].class);
        } catch (Exception e) {
            throw new RuntimeException("Unable to get Unsafe instance", e);
        }
    }

    public static void main(String[] args) {
        // 分配 10 个字节的内存
        long memoryAddress = unsafe.allocateMemory(10L);
        try {
            // 在内存中写入值
            for (int i = 0; i < 10; i++) {
                unsafe.putByte(memoryAddress + i, (byte) (i + 1));
            }

            // 从内存中读取值
            for (int i = 0; i < 10; i++) {
                byte value = unsafe.getByte(memoryAddress + i);
                System.out.println("Value at index " + i + ": " + value);
            }

            // 使用 Unsafe 访问数组元素
            byte[] byteArray = new byte[10];
            for (int i = 0; i < 10; i++) {
                unsafe.putByte(byteArray, BYTE_ARRAY_BASE_OFFSET + i, (byte) (i + 11));
            }
            for (int i = 0; i < 10; i++) {
                byte value = unsafe.getByte(byteArray, BYTE_ARRAY_BASE_OFFSET + i);
                System.out.println("Array value at index " + i + ": " + value);
            }

        } finally {
            // 释放内存
            unsafe.freeMemory(memoryAddress);
        }
    }
}

2. 内存屏障
(1)内存屏障(Memory Barrier)是用于防止编译器和 CPU 对指令的重新排序,以确保特定的操作顺序对所有处理器和线程都是一致的。
(2) Unsafe类提供的实现内存屏障的方法如下:

  • storeFence():确保在内存屏障之前的所有存储操作在屏障之后的存储操作之前完成。
  • loadFence():确保在内存屏障之前的所有加载操作在屏障之后的加载操作之前完成。
  • fullFence():确保在内存屏障之前的所有存储和加载操作在屏障之后的存储和加载操作之前完成。

(3)示例:

public class UnsafeMemoryBarrierExample {

    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("Unable to get Unsafe instance", e);
        }
    }

    private volatile int sharedVar = 0;

    public void storeFenceExample() {
        // 写操作前插入 storeFence
        unsafe.storeFence();
        sharedVar = 42;
        unsafe.storeFence();
        // 确保所有存储操作在此之前完成
    }

    public void loadFenceExample() {
        unsafe.loadFence();
        int value = sharedVar;
        unsafe.loadFence();
        // 确保所有加载操作在此之后完成
    }

    public void fullFenceExample() {
        unsafe.fullFence();
        sharedVar = 42;
        int value = sharedVar;
        unsafe.fullFence();
        // 确保所有存储和加载操作在此之前完成
    }

    public static void main(String[] args) {
        UnsafeMemoryBarrierExample example = new UnsafeMemoryBarrierExample();

        example.storeFenceExample();
        example.loadFenceExample();
        example.fullFenceExample();
    }
}

(4)Unsafe类实现内存屏障和Volatile关键字区别

特性Unsafe 类的内存屏障volatile 关键字
用途手动控制内存操作顺序确保变量的可见性和有序性
实现复杂度高,需要手动插入内存屏障低,通过关键字声明
内存屏障类型storeFence(), loadFence(), fullFence()内置,隐式插入
代码可读性低,可读性差,难以维护高,简洁明了
编译器重排序防护是,通过显式屏障是,通过内置机制
CPU重排序防护是,通过显式屏障是,通过内置机制
使用场景高性能、低延迟需求,需精准控制的场合一般并发编程,需确保变量可见性的场合
跨平台兼容性低,依赖具体 JVM 实现,不保证一致性高,由 JVM 提供保障
线程安全是,但需手动确保正确性是,由关键字保证
内存开销较低,只影响屏障附近代码较高,影响整个变量的读写
典型用法高性能并发库,如 java.util.concurrent通用多线程应用
故障风险高,不正确使用可能导致崩溃或数据损坏低,由 JVM 保证

(3)CAS 操作
(1) CAS(Compare-And-Swap)操作是一种原子操作,用于实现无锁并发编程,是一种乐观锁的形式。
(2) Unsafe 类提供了多种 CAS 方法,包括compareAndSwapInt、compareAndSwapLong 和 compareAndSwapObject。
(3)Java中的synchronized关键字和抽象队列同步器的底层实现都使用到了CAS操作。
(4)示例:

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);

            // 获取 value 字段的偏移量
            valueOffset = unsafe.objectFieldOffset(UnsafeCASExample.class.getDeclaredField("value"));
        } catch (Exception e) {
            throw new RuntimeException("Unable to get Unsafe instance", e);
        }
    }

    private volatile int value;

    public UnsafeCASExample(int initialValue) {
        this.value = initialValue;
    }

    public int getValue() {
        return value;
    }

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

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

        // 多线程更新 value
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                int oldValue;
                int newValue;
                do {
                    oldValue = example.getValue();
                    newValue = oldValue + 1;
                } while (!example.compareAndSwapValue(oldValue, newValue));
            }
        };

        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        System.out.println("Final value: " + example.getValue());
    }
}

4. 线程调度
(1) Unsafe 类不仅提供了底层的内存操作,还提供了一些与线程操作相关的方法,包括暂停、恢复和阻塞线程等。

  • park:阻塞当前线程,直到其他线程将其唤醒
  • unpark:唤醒被阻塞的线程
    :monitorEnter、monitorExit、tryMonitorEnter方法三个方法已经被弃用
    (2)示例
public class UnsafeThreadExample {

    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("Unable to get Unsafe instance", e);
        }
    }

    public static void main(String[] args) {
        Thread parker = new Thread(() -> {
            System.out.println("Thread is going to park");
            unsafe.park(false, 0);
            System.out.println("Thread is unparked");
        });

        Thread unparker = new Thread(() -> {
            try {
                Thread.sleep(1000); // Sleep for a while before unparking
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("Thread will unpark the parked thread");
            unsafe.unpark(parker);
        });

        parker.start();
        unparker.start();

        try {
            parker.join();
            unparker.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

5. 其他功能
Unsafe还提供一些其他的功能,包括:对象操作、数据操作、Class 操作、系统信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值