J.U.C - 基础工具类:Unsafe类
文章目录
Unsafe 类简介
Java不能像C/C++一样直接操作内存区域,需要通过本地方法的方式来操作内存区域。JDK可以通过一个“后门”——Unsafe类,执行底层硬件级别的CAS原子操作,线程阻塞和唤醒等。
Unsafe
类是 Java 中一个非常特殊的类,它提供了硬件级别的原子操作和直接内存访问等功能。
Unsafe类中的方法几乎全部都是native方法,它们使用JNI的方式调用本地的C++类库。由于其强大的功能和潜在的风险,Unsafe
类并未公开暴露给普通开发者,而是被隐藏在 sun.misc
包中。
主要功能
-
内存操作
allocateMemory(long bytes)
: 分配指定大小的内存。freeMemory(long address)
: 释放指定地址的内存。getLong(long address)
: 从指定地址读取长整型值。putLong(long address, long value)
: 将长整型值写入指定地址。
-
对象操作
allocateInstance(Class<T> cls)
: 分配一个新的对象实例,但不调用构造函数。copyMemory(Object src, long srcOffset, Object dest, long destOffset, long bytes)
: 复制内存。objectFieldOffset(Field field)
: 获取字段在对象中的偏移量。
-
数组操作
arrayBaseOffset(Class<?> arrayClass)
: 获取数组的基地址偏移量。arrayIndexScale(Class<?> arrayClass)
: 获取数组元素的大小。
-
线程操作
park(boolean isAbsolute, long time)
: 挂起当前线程。unpark(Thread thread)
: 唤醒指定线程。
-
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
}
}
在多线程环境中非常有用,可以确保操作的原子性和线程安全性
注意事项
- 安全性:
Unsafe
类的操作绕过了 JVM 的安全检查,不当使用可能导致内存泄漏、数据损坏等问题。 - 兼容性:
Unsafe
类是 JDK 内部实现的一部分,不同版本的 JDK 可能会有不同的行为,甚至在未来版本中可能会被移除。 - 替代方案:对于大多数应用场景,推荐使用
java.util.concurrent
包中的工具类,如AtomicInteger
、AtomicLong
等,它们提供了更安全、更稳定的并发操作。
原文地址:https://blog.csdn.net/yangshangwei/article/details/143749935
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!