本文对Java中的Unsafe类及其常见功能进行总结。
一、介绍
Unsafe类主要用于执行低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等,其实现依赖于本地方法(Native method,用其他语言(如C/C++/汇编)编写的)
二、Unsafe类的使用
- Unsafe 类为一单例实现,提供静态方法 getUnsafe 获取 Unsafe实例。但由于其操作非常底层,为了保证安全性,只有通过启动类加载器加载的类才能够调用 Unsafe 类中的方法。
- 非启动类加载器加载类使用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 操作、系统信息。