从新的 JNI 线程通知 Java
Notify Java from new JNI thread
我制作了一个 Java 调用 JNI 的程序,在 JNI 中它创建了一个“JNICallback”线程并在其中执行 env-attachCurrentThreadAsDaemon()
我的目标是从另一个纯 Java 线程 JNICallback 线程通知。
我做一些标准的事情,比如 synchronized -> wait, synchronized -> notifyAll
,使用 static final Object
作为同步。
如果我 运行 它在 Eclipse 下,一切正常。
但是如果我将它导出为“运行nable jar 文件”和 运行 在同一个 JVM 下,我看到我的 JNI 程序调用 monitorEnter, notifyAll, monitorExit
没有错误,但是 Java 没有通知。
在调试器中,我看到 JNI 和 Java 使用的同一个对象有不同的 ObjectId,这对我来说很可疑(它是下面代码中的名称 onConnectEvent)
我什至将通知代码从 JNI 移动到 Java,并使用 JNI 的静态无效方法调用,在 Java 中执行同步->notifyAll,但这只告诉我等待线程在不同的 ObjectId 上等待,所以估计是这个原因。
...
static final Object onConnectEvent = new Object();
...
Thread dogServerConnect,...;
...
public MainClass(){
dogServerConnect = runDog(new ServerConnectionWatchDog(), "onServerConnect");
someJNIstuffPlusNewThreadsCreation();
...
static void onJNICallbackEvent(int type) {
switch (type) {
case 1 -> {
synchronized(onConnectEvent) {
onConnectEvent.notifyAll();
^^^^^ got to this line from JNI, just fine
}
...
class ServerConnectionWatchDog implements Runnable {
@Override
public void run() {
while(true)
synchronized(onConnectEvent) {
try {
onConnectEvent.wait();
for (var l : listeners) {
^^^^^^^^^^^^^^^^^^^^^^^^^ never come to this line
...
同样,这在 Eclipse 下确实运行良好,但由于某些原因作为单独的 jar 文件失败。
为什么同一个 final 静态对象对于不同的线程可能有不同的实例 id,或者现在这样就好了?
我的意思是对于等待线程,Eclipse 向我显示“正在等待:对象 (id=48)”,对于在“notifyAll”执行之前的本机线程,它向我显示“拥有:对象 (id=50)”。也许这是因为我手动暂停了等待线程,以查看对象 ID(然后 运行 再次出现,这不是原因)
我能错过什么?
在 Android 的常见问题解答中找到了答案:why didn't FindClass find my class。
虽然我的环境不是Android,但还是有道理的。
事情是 env->FindClass 展开 Java 堆栈以找到调用者 class 并使用它的 classloader。
对于在 JNI 中启动的本机线程,Java 堆栈是空的。因此,这给了我们唯一的选择,要么在创建本机线程之前获取对所需 classes 的全局引用,要么在同一位置获取对其 classloader 的引用,然后使用 FindClass 来自保存了 classLoader,比较难。
此解决方案可防止 Eclipse JarRsrcLoader 创建重复的静态对象。
可能有人会说在 JNI 中新创建的本机线程中执行 env->FindClass 是一个糟糕的设计,至少要谨慎使用。
我制作了一个 Java 调用 JNI 的程序,在 JNI 中它创建了一个“JNICallback”线程并在其中执行 env-attachCurrentThreadAsDaemon()
我的目标是从另一个纯 Java 线程 JNICallback 线程通知。
我做一些标准的事情,比如 synchronized -> wait, synchronized -> notifyAll
,使用 static final Object
作为同步。
如果我 运行 它在 Eclipse 下,一切正常。
但是如果我将它导出为“运行nable jar 文件”和 运行 在同一个 JVM 下,我看到我的 JNI 程序调用 monitorEnter, notifyAll, monitorExit
没有错误,但是 Java 没有通知。
在调试器中,我看到 JNI 和 Java 使用的同一个对象有不同的 ObjectId,这对我来说很可疑(它是下面代码中的名称 onConnectEvent)
我什至将通知代码从 JNI 移动到 Java,并使用 JNI 的静态无效方法调用,在 Java 中执行同步->notifyAll,但这只告诉我等待线程在不同的 ObjectId 上等待,所以估计是这个原因。
...
static final Object onConnectEvent = new Object();
...
Thread dogServerConnect,...;
...
public MainClass(){
dogServerConnect = runDog(new ServerConnectionWatchDog(), "onServerConnect");
someJNIstuffPlusNewThreadsCreation();
...
static void onJNICallbackEvent(int type) {
switch (type) {
case 1 -> {
synchronized(onConnectEvent) {
onConnectEvent.notifyAll();
^^^^^ got to this line from JNI, just fine
}
...
class ServerConnectionWatchDog implements Runnable {
@Override
public void run() {
while(true)
synchronized(onConnectEvent) {
try {
onConnectEvent.wait();
for (var l : listeners) {
^^^^^^^^^^^^^^^^^^^^^^^^^ never come to this line
...
同样,这在 Eclipse 下确实运行良好,但由于某些原因作为单独的 jar 文件失败。 为什么同一个 final 静态对象对于不同的线程可能有不同的实例 id,或者现在这样就好了?
我的意思是对于等待线程,Eclipse 向我显示“正在等待:对象 (id=48)”,对于在“notifyAll”执行之前的本机线程,它向我显示“拥有:对象 (id=50)”。也许这是因为我手动暂停了等待线程,以查看对象 ID(然后 运行 再次出现,这不是原因) 我能错过什么?
在 Android 的常见问题解答中找到了答案:why didn't FindClass find my class。 虽然我的环境不是Android,但还是有道理的。 事情是 env->FindClass 展开 Java 堆栈以找到调用者 class 并使用它的 classloader。 对于在 JNI 中启动的本机线程,Java 堆栈是空的。因此,这给了我们唯一的选择,要么在创建本机线程之前获取对所需 classes 的全局引用,要么在同一位置获取对其 classloader 的引用,然后使用 FindClass 来自保存了 classLoader,比较难。 此解决方案可防止 Eclipse JarRsrcLoader 创建重复的静态对象。 可能有人会说在 JNI 中新创建的本机线程中执行 env->FindClass 是一个糟糕的设计,至少要谨慎使用。