我编写了一个调用JNI的Java程序,在JNI中它创建了一个"JNICallback“线程,并在其中执行env-attachCurrentThreadAsDaemon()
我的目标是从Java线程通知,这是另一个纯JNICallback线程。我使用static final Object作为同步,做一些标准的事情,比如synchronized -> wait, synchronized -> notifyAll。如果我在Eclipse下运行它,一切都会正常工作。
但是如果我将其导出为"runnable jar文件“并在相同的JVM下运行,我会看到我的JNI程序调用monitorEnter, notifyAll, monitorExit时没有错误,但是Java没有通知。在调试器中,我看到来自JNI和Java的同一个对象使用了不同的ObjectId,这对我来说很可疑(下面代码中的名称是onConnectEvent )我甚至将通知代码从JNI转移到Java,并使用JNI中的静态void方法调用,在Java语言中执行synchronize->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文件失败。为什么相同的最终静态对象可以有不同的实例it用于不同的线程,或者现在很好?
我的意思是,对于等待线程,Eclipse显示"waiting for: Object (id=48)“,对于执行"notifyAll”之前的本机线程,它显示“拥有: Object (id=50)”。也许这是因为我手动暂停了等待线程,以查看对象id (然后再次运行它,这不是原因),我可能会错过什么?
发布于 2021-11-17 21:43:12
在Android的常见问题解答中找到了答案:why didn't FindClass find my class。虽然我的环境不是Android,但它仍然有意义。问题是env->FindClass展开一个Java堆栈来找到一个调用者类并使用它的类加载器。对于在JNI中启动的本机线程,Java堆栈是空的。因此,这给了我们一个唯一的选择,要么在创建本机线程之前获取对所需类的全局引用,要么在同一位置获取对其类加载器的引用,然后使用保存的classLoader中的FindClass,这是比较困难的。这个解决方案可以防止Eclipse JarRsrcLoader创建重复的静态对象。也许有人会说,在JNI中新创建的本机线程中执行env->FindClass是一个糟糕的设计,至少要谨慎使用。
https://stackoverflow.com/questions/69883285
复制相似问题