java的unsafe类和varhandle类

发布时间 2023-12-07 14:09:56作者: 过移

1、如何从unsafe类获取对象

private Unsafe() {
}

@CallerSensitive
public static Unsafe getUnsafe() {
Class<?> caller = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(caller.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
从中可以看出,无法直接new出来,并且getUnsafe方法也不是给我们使用的。

2、用反射的方式从unsafe类中获取对象

由于源码里面有

private static final Unsafe theUnsafe;

所以我们可以使用:

public static Unsafe reflectGetUnsafe(){
try {
Field unsafe = Unsafe.class.getDeclaredField("theUnsafe");
unsafe.setAccessible(true);
return (Unsafe) unsafe.get(null);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}

3、拿到了unsafe能做什么?

1、它能修改对象地址里面的元素信息

2、它可以自己开辟一段空间用来存东西

3、它提供了原子类修改方法

4、它可以绕开java的构造器来创建对象

package memory.unsafeTest;

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

public class UnsafeTest {
public static void main(String[] args) throws InstantiationException {
Unsafe unsafe = reflectGetUnsafe();
Message message=new Message(1,"你好");
int anInt = unsafe.getInt(message, 0);
System.out.println(anInt);
long address = unsafe.allocateMemory(4); // 分配4个字节的内存空间
unsafe.putInt(address, 21); // 将值存储到该地址上
System.out.println(unsafe.getInt(address)); // 输出:21

Message demo = (Message) unsafe.allocateInstance(Message.class);
System.out.println(demo.id); // 输出:0

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

}

public static Unsafe reflectGetUnsafe(){
try {
Field unsafe = Unsafe.class.getDeclaredField("theUnsafe");
unsafe.setAccessible(true);
return (Unsafe) unsafe.get(null);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}

Message类:

package memory.unsafeTest;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Message {
public int id=9;
public String name;
}

输出结果:

 

4、那varhandle类有什么用?

因为unsafe是不安全的且只许那些编程java的人写,所以就搞了一种更安全的类,此类可以让所有人使用

具体用法:

VarHandle xHandle = MethodHandles.lookup().in(Message.class).findVarHandle(Message.class, "id", int.class);

这个方法的作用是获取Message类中名为"id"的int类型变量的VarHandle对象。VarHandle是Java 9中引入的一种新的机制,用于在不使用反射的情况下直接访问Java对象的字段和数组元素。通过VarHandle,可以实现对变量的原子性操作,而不需要使用synchronized或者Lock等同步机制。在这个例子中,通过xHandle可以直接访问Message类中的"id"变量,从而实现对该变量的原子性操作。

为什么都要提原子操作呢?因为这个类发明的初衷就是:

随着 Java 中的并发和并行编程的不断扩展,程序员 由于无法使用 Java 结构来安排,他们越来越感到沮丧 对单个类的字段进行原子或有序操作;例如 以原子方式递增字段。到目前为止,实现的唯一方法 这些效果是使用独立的(添加两个空间 间接管理的开销和其他并发问题)或在某些方面 情况,使用原子 s(经常遇到更多的开销 比操作本身),或者使用不安全的(和不可移植的和 不支持)用于 JVM 内部函数的 API。内部函数更快, 因此,它们已被广泛使用,损害了安全性和便携性。countAtomicIntegerFieldUpdatersun.misc.Unsafe

如果没有这个 JEP,这些问题预计会随着原子 API 而变得更糟 展开以涵盖其他访问一致性策略(与最近的 C++ 内存模型)作为 Java 内存模型修订版的一部分

 参考: JEP 193:可变句柄 (openjdk.org)                                                                                                                                            

5、如何使用varhandle类

package memory.varhandleTest;

import memory.unsafeTest.Message;

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;

public class VarHandleTest {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
// 获取VarHandle对象
VarHandle xHandle = MethodHandles.lookup().in(Message.class).findVarHandle(Message.class, "id", int.class);
System.out.println(xHandle);
// 创建Message对象
Message message = new Message();
// 获取id字段的值
int o = (int)xHandle.get(message);
// 将id字段的值设置为2
xHandle.set(message, 2);
// 再次获取id字段的值
int i = (int)xHandle.get(message);
// 打印结果
System.out.println(o);
System.out.println(i);
}
}

 结果:

 它里面的原子操作:

比如说getVolatile这个方法和setVolatile这个方法到底是不是原子操作,就交由你们完成了0.0