java - 有人可以解释如下代码吗?
问题描述
import sun.misc.Unsafe;
import sun.nio.ch.DirectBuffer;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class PingPongMapMain {
public static void main(String... args) throws IOException {
boolean odd;
switch (args.length < 1 ? "usage" : args[0].toLowerCase()) {
case "odd":
odd = true;
break;
case "even":
odd = false;
break;
default:
System.err.println("Usage: java PingPongMain [odd|even]");
return;
}
int runs = 10000000;
long start = 0;
System.out.println("Waiting for the other odd/even");
File counters = new File(System.getProperty("java.io.tmpdir"),"counters.deleteme");
counters.deleteOnExit();
try (FileChannel fc = new RandomAccessFile(counters, "rw").getChannel()) {
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
long address = ((DirectBuffer) mbb).address();
for (int i = -1; i < runs; i++) {
for (; ; ) {
long value = UNSAFE.getLongVolatile(null, address);
boolean isOdd = (value & 1) != 0;
if (isOdd != odd)
// wait for the other side.
continue;
// make the change atomic, just in case there is more than one odd/even process
if (UNSAFE.compareAndSwapLong(null, address, value, value + 1))
break;
}
if (i == 0) {
System.out.println("Started");
start = System.nanoTime();
}
}
}
System.out.printf("... Finished, average ping/pong took %,d ns%n",
(System.nanoTime() - start) / runs);
}
static final Unsafe UNSAFE;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
UNSAFE = (Unsafe) theUnsafe.get(null);
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
这个例子展示了“线程安全访问直接内存”,它用于在进程之间共享数据。当在两个程序中运行这个时,一个是奇数,另一个是偶数。您可以看到每个进程都在通过持久共享内存更改数据。
但我对一些事情感到困惑:
- tmp 文件的用途是什么;
- UnSafe 在这里有什么用;
- 为什么在这里不安全使用;
- 为什么将字段转换为 UnSafe 类型而不是 sun.misc.Unsafe.getUnsafe()?
解决方案
该程序说明了两个独立的 Java 进程如何通过内存映射文件实现的共享内存段进行通信。一个进程将共享段中某个位置的数字设为奇数,而另一个进程将其设为偶数……重复……两个进程之间的“乒乓”。
tmp 文件是做什么用的
它有效地为两个进程共享一个内存段提供了一种方式。创建一个文件,并将其映射到两个进程的地址空间。最终结果是两个进程共享相同的内存区域。
UnSafe 在这里有什么用;
long value = UNSAFE.getLongVolatile(null, address);
address
使用读取屏障读取 64 位;即确保它正在从主内存(而不是高速缓存)读取
UNSAFE.compareAndSwapLong(null, address, value, value + 1));
执行原子比较和交换。如果 at 的address
值为value
,则自动将其更改为value + 1
。
为什么在这里不安全使用;
因为它是执行那些低级操作的好方法(恰好具有这些语义。(当使用它们的线程位于单独的进程中时,Java 原始互斥锁之类的东西并没有Lock
指定“Java 内存模型”语义。)
为什么将字段转换为 UnSafe 类型而不是 sun.misc.Unsafe.getUnsafe()?
这是一些旨在绕过 JVM 限制的讨厌的东西。如果你只是打电话Unsafe.getUnsafe()
,你通常会得到一个SecurityException
.
参考:
只是看另一边......
1 - 可能是除了编写不可移植的本机代码之外的唯一方法。但请记住,Unsafe
该类是为 JVM 内部使用而设计的,API 和行为可能会在没有任何通知的情况下发生变化。
推荐阅读
- r - 有条件的按月计算
- sql-server - 使用 Python 将 SQL 数据输出到文本文件 - 如何摆脱无?
- axon - 如何通过使用来自 kafka 主题的消息而不是通过 Rest API 创建命令
- json - 从 sql 列值更新 json 值
- c# - 对 SOAP Web 服务调用使用更多的 OOP 方法而不是 switch 语句
- javascript - 在另一个函数中调用一个函数被转移到另一个组件
- python - 由于几何形状不同,尝试溶解多边形时使用 fiona 的代码失败
- r - 如何从数据框列表中折叠到具有最小 int 值的数据框
- docker-container - 以普通用户身份运行 OCI 容器
- javascript - 我们可以简化这个速记 if/else