3回复
3年前
用 JNI 创建 JFrame 的死锁
我有一个需要从C++调用的Java库,我用JNI调用了我需要的方法,但是出现了一个陷入奇怪死锁的方法。
这个有问题的方法在工作线程上创建了一个JFrame,从C++调用时它不返回。我谷歌了一下,我发现一篇文章里面写,在MacOS里,用JNI的时候,任何跟Swing/awt的交互都不能从主线程完成。我改成从工作线程调用该方法之后,jvm还是卡在死锁中并且JNI调用还是不返回。
我的代码大概是这样(简化了一下但基本相同):
Java代码:
import javax.swing.*;
import java.awt.*;
public class MyClass
{
private JFrame frame;
private Thread thread;
public void createFrame()
{
thread = new Thread(() -> {
frame = new JFrame("My Frame");
frame.setTitle("JFrameCenter Position");
frame.add(new JLabel("JFrame set to center of the screen", SwingConstants.CENTER), BorderLayout.CENTER);
frame.setSize(400, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
System.out.println("Exception on join Thread");
e.printStackTrace();
}
}
}
C++代码:
#include <iostream>
#include <jni.h>
#include <dlfcn.h>
#define JLI_DYLIB "/lib/jli/libjli.dylib"
#define JNI_CREATEVM_FUNC "JNI_CreateJavaVM"
typedef jint (JNICALL CreateJavaVM_t)(JavaVM **pvm, void **env, void *args);
void* DyLibHandler;
JavaVM *jvm;
void *GetStartJvmFunc(std::string javaHome) {
std::string jliPath = javaHome.append(JLI_DYLIB);
DyLibHandler = dlopen(jliPath.c_str(), RTLD_LAZY);
if(!DyLibHandler)
{
return NULL;
}
void * jvmFunc = dlsym(DyLibHandler, JNI_CREATEVM_FUNC);
if (!jvmFunc)
{
dlclose(DyLibHandler);
return NULL;
}
return jvmFunc;
}
void StartJvm(const char * javaHome, const char * classPath) {
std::string jarsPath = "-Djava.class.path=";
std::string home = "-Djava.home=";
JavaVMOption* options = new JavaVMOption[3]; // JVM invocation options
options[0].optionString = (char*)jarsPath.append(classPath).c_str();
options[0].extraInfo = NULL;
options[1].optionString = (char*)home.append(javaHome).c_str();
options[1].extraInfo = NULL;
options[2].optionString = "-Dsun.net.inetaddr.ttl=10";
options[2].extraInfo = NULL;
JavaVMInitArgs vm_args;
vm_args.options = options;
vm_args.nOptions = 3;
vm_args.version = JNI_VERSION_1_8;
JNIEnv *jniEnvironment;
CreateJavaVM_t* createVMFunc = (CreateJavaVM_t*)GetStartJvmFunc(std::string(javaHome));
jint rc = createVMFunc(&jvm, (void**)&jniEnvironment, &vm_args);
delete[] options;
if (rc != JNI_OK) {
createVMFunc = NULL;
exit(EXIT_FAILURE);
}
}
void CreateJFrame(JNIEnv *jniEnvironment)
{
jclass localHandler = jniEnvironment->FindClass("MyClass");
if (!localHandler)
{
std::cout << "Could not Find LocalClass" << std::endl;
exit(EXIT_FAILURE);
}
jclass classHandle = (jclass) jniEnvironment->NewGlobalRef(localHandler);
jniEnvironment->DeleteLocalRef(localHandler);
jmethodID constructorHandler = jniEnvironment->GetMethodID(classHandle, "<init>", "()V");
if (!constructorHandler)
{
std::cout << "Could not find Constructor" << std::endl;
exit(EXIT_FAILURE);
}
jmethodID createMethodHandler = jniEnvironment->GetMethodID(classHandle, "createFrame", "()V");
if (!createMethodHandler)
{
std::cout << "Could not Find CreateFrame Method" << std::endl;
exit(EXIT_FAILURE);
}
jobject localRef = jniEnvironment->NewObject(classHandle, constructorHandler);
if(!localRef)
{
std::cout << "Could not instantiate MyClass" << std::endl;
exit(EXIT_FAILURE);
}
jobject classInstance = jniEnvironment->NewGlobalRef(localRef);
jniEnvironment->DeleteLocalRef(localRef);
if (classInstance == nullptr)
{
std::cout << "Could not create global ref to MyClass instance" << std::endl;
exit(EXIT_FAILURE);
}
jniEnvironment->CallVoidMethod(classInstance, createMethodHandler);
}
void SourceCallback ( void *info ) {
std::cout << "From Source Callback! Got Event" << std::endl;
}
void * ThreadProc(void * arg)
{
JNIEnv *env = NULL;
jint result =jvm->AttachCurrentThread((void **)&env, NULL);
if( result != JNI_OK)
{
std::cout << "Error! Could not attach JNIEnv to thread (" << result << ")"<< std::endl;
goto clean;
}
CreateJFrame(env);
std::cout << "Deataching Thread" << std::endl;
result = jvm->DetachCurrentThread();
if(result != JNI_OK)
{
std::cout << "Error! could not deatached current thread (" << result << ")" << std::endl;
}
else{
std::cout << "Done Deataching Thread" << std::endl;
}
clean:
std::cout << "Done Creating Frame" << std::endl;
return NULL;
}
int main(int argc, const char * argv[])
{
StartJvm(/*Java_Home path*/, /*Path to MyClass.class*/);
pthread_t vmThread;
pthread_create(&vmThread, NULL, ThreadProc, NULL);
pthread_join(vmThread, NULL);
std::cout << "UIThread is done" << std::endl;
std::cout << "Waiting to Terminate program" << std::endl;
std::string str;
std::getline(std::cin, str);
}
我想用Jstack看下为什么会导致死锁,但我不确定是什么原因。 这是 JStack:
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.291-b10 mixed mode):
"Thread-1" #11 prio=5 os_prio=31 tid=0x00007f88a70b0800 nid=0x3e03 runnable [0x0000700002a51000]
java.lang.Thread.State: RUNNABLE
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1934)
- locked <0x000000076ab068b0> (a java.util.Vector)
- locked <0x000000076ab06908> (a java.util.Vector)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1817)
at java.lang.Runtime.load0(Runtime.java:810)
- locked <0x000000076ab1abb0> (a java.lang.Runtime)
at java.lang.System.load(System.java:1086)
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1934)
- locked <0x000000076ab068b0> (a java.util.Vector)
- locked <0x000000076ab06908> (a java.util.Vector)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1838)
at java.lang.Runtime.loadLibrary0(Runtime.java:871)
- locked <0x000000076ab1abb0> (a java.lang.Runtime)
at java.lang.System.loadLibrary(System.java:1122)
at java.awt.Toolkit$3.run(Toolkit.java:1636)
at java.awt.Toolkit$3.run(Toolkit.java:1634)
at java.security.AccessController.doPrivileged(Native Method)
at java.awt.Toolkit.loadLibraries(Toolkit.java:1633)
at java.awt.Toolkit.<clinit>(Toolkit.java:1670)
at java.awt.Component.<clinit>(Component.java:593)
at MyClass.lambda$createFrame$0(MyClass.java:11)
at MyClass$$Lambda$1/1923699808.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Thread-0" #10 prio=5 os_prio=31 tid=0x00007f88a804d000 nid=0x4003 in Object.wait() [0x0000700002951000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076adaaa58> (a java.lang.Thread)
at java.lang.Thread.join(Thread.java:1252)
- locked <0x000000076adaaa58> (a java.lang.Thread)
at java.lang.Thread.join(Thread.java:1326)
at MyClass.createFrame(MyClass.java:21)
"Service Thread" #9 daemon prio=9 os_prio=31 tid=0x00007f88a7010000 nid=0x4203 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread3" #8 daemon prio=9 os_prio=31 tid=0x00007f88a8034800 nid=0x4403 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread2" #7 daemon prio=9 os_prio=31 tid=0x00007f88a7017800 nid=0x4603 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #6 daemon prio=9 os_prio=31 tid=0x00007f88a7829800 nid=0x3503 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #5 daemon prio=9 os_prio=31 tid=0x00007f88a7016800 nid=0x4903 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007f88a8008800 nid=0x3303 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007f88a8013000 nid=0x5203 in Object.wait() [0x00007000020b3000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076ab08ee0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x000000076ab08ee0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007f88a8010000 nid=0x5403 in Object.wait() [0x0000700001fb0000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076ab06c00> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x000000076ab06c00> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"main" #1 prio=5 os_prio=31 tid=0x00007f88a9008800 nid=0x307 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"VM Thread" os_prio=31 tid=0x00007f88a800b800 nid=0x2a03 runnable
"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007f88a780c800 nid=0x2707 runnable
"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007f88a780d800 nid=0x2503 runnable
"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007f88a9808800 nid=0x2403 runnable
"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007f88a7015800 nid=0x2203 runnable
"GC task thread#4 (ParallelGC)" os_prio=31 tid=0x00007f88aa008800 nid=0x2003 runnable
"GC task thread#5 (ParallelGC)" os_prio=31 tid=0x00007f88aa808800 nid=0x1a03 runnable
"GC task thread#6 (ParallelGC)" os_prio=31 tid=0x00007f88a7016000 nid=0x1c03 runnable
"GC task thread#7 (ParallelGC)" os_prio=31 tid=0x00007f88a8009800 nid=0x1d03 runnable
"VM Periodic Task Thread" os_prio=31 tid=0x00007f88a783c800 nid=0x4103 waiting on condition
JNI global references: 317
Heap
PSYoungGen total 76288K, used 7868K [0x000000076ab00000, 0x0000000770000000, 0x00000007c0000000)
eden space 65536K, 12% used [0x000000076ab00000,0x000000076b2af378,0x000000076eb00000)
from space 10752K, 0% used [0x000000076f580000,0x000000076f580000,0x0000000770000000)
to space 10752K, 0% used [0x000000076eb00000,0x000000076eb00000,0x000000076f580000)
ParOldGen total 175104K, used 0K [0x00000006c0000000, 0x00000006cab00000, 0x000000076ab00000)
object space 175104K, 0% used [0x00000006c0000000,0x00000006c0000000,0x00000006cab00000)
Metaspace used 4266K, capacity 4704K, committed 4992K, reserved 1056768K
class space used 474K, capacity 525K, committed 640K, reserved 1048576K
我看到Thread-0在等待调用Thread.join,但是工作线程 (Thread-1) 正在等待加载本机库(java.lang.ClassLoader$NativeLibrary.load(Native Method) ) ,但这并没有发生,程序只是挂在那里,直到我终止进程。
我想通过前面的代码显示JFrame,在控制台查看最后两个打印,还有等待控制台中的输入终止的程序。但是就像我之前所说的那样,既没有显示 JFrame,也没有发生打印。
请问有大神知道为什么会发生这种情况吗?
3057 阅读