我的 JNI 函数返回 ProcessedImage 对象。Java中的相关类定义:
public class Thumbnail {
public int size;
public byte[] data;
}
public class ProcessedImage {
public Thumbnail[] thumbnails;
public byte[] hash;
}
我的本机函数声明如下所示:
public class ImageProcessor {
// Called from different JVM threads.
public static native ProcessedImage processImage(byte[] image);
// Only called once.
public static native initialize();
static {
System.loadLibrary("ImageProcessor");
initialize();
}
}
现在我想编写一个本机初始化函数,它执行以下操作:
- 在本土填充全局矩阵。该矩阵稍后会在我的 processImage 本机函数中读取,但在此初始化后是不可变的。
- 调用 GraphicsMagick 初始化函数。
(2) 很容易,因为它需要调用一次。我不确定如何处理(1)。我可以在这里初始化数组,但它可能对调用 processImage 函数的其他 Java 线程不可见。我可以假设因为这是在一个静态块中,所以它将在创建任何其他 JVM 线程之前执行?这似乎是一个错误的假设。对我来说,确保该数组稍后对其他 JVM 线程可见的最佳方法是什么?我不想使用锁定来访问/写入这个矩阵。如果这是纯 C/C++,我会做这样的事情:
__attribute__((constructor))
static void Initialize() {
// Initialize everything here.
}
另一个问题是缓存 jclasses 和 jfieldID 的最佳位置是什么?我是否应该在初始化函数中查找它们并将它们存储为全局变量。我读到这不是一个好习惯,因为 jclass 和 jfieldIDs 在类被卸载并再次加载后可能会过时。标准做法似乎应该在类中的静态块中执行此操作。像这样的东西:
public class Thumbnail {
private static native initializeThumbnailNative();
static {
initializeThumbnailNative();
}
public int size;
public byte[] data;
}
由于一个类可以在与调用我的本机函数 processImage 的线程不同的线程上加载和卸载,因此我的本机函数可能看不到这些全局 jclasses 和 jfieldID 的更改。处理此问题的推荐方法是什么。我可以再次锁定对这些字段的访问并在异常时重试本机方法,但这似乎不正确。另一种可能可行的hacky方法是我可以在java中创建所有类的单个实例并将它们存储为静态,这样这些类总是至少有一个实例并且它们永远不会被卸载。所以是这样的:
public class ImageProcessor {
public static native ProcessedImage processImage(byte[] image);
public static native initialize();
// Create a static instance to prevent these classes from being unloaded.
private static Thumbnail thumbnail = new Thumbnail();
private static ProcessedImage processedImage = new ProcessedImage();
static {
System.loadLibrary("ImageProcessor");
initialize();
}
}
我不确定这是否可行。在多线程应用程序中实现 jclasses 和 jFieldID 缓存的最佳方法是什么?我提前为这个冗长的问题道歉,但我想提供最大的背景信息。