2012-01-05 71 views
6

我真的很陌生,因此,如果這聽起來像一個愚蠢的問題,我提前道歉。OSX:JavaVM,AWT/Swing和可能的死鎖

我正在嘗試構建一個用普通C編寫的簡單應用程序,它必須創建一個JavaVM,然後通過基於AWT/Swing加載java代碼來創建一個新窗口。

正在關注this技術說明我知道只有在Mac OSX中,必須從與主線程不同的線程調用JavaVM才能創建基於AWT的GUI。

因此,在我的C應用程序的main函數中,我創建了一個新線程來執行從javaVM創建到創建GUI的所有內容。

由於應用程序並非如此簡單,我將發佈一個簡化版本。

主要功能:

int main(int argc, char** argv) 
{ 

    // Run-time loading of JavaVM framework 

    void *result; 

    result = dlopen("/System/Library/Frameworks/JavaVM.framework/JavaVM", RTLD_LAZY); 
    if (!result) { 
     printf("can't open library JavaVM: %s\n", dlerror()); 
    } 
    else { 
     printf("library JavaVM loaded\n"); 
    } 

    /* Start the thread that runs the VM. */ 
    pthread_t vmthread; 

    // create a new pthread copying the stack size of the primordial pthread 
    struct rlimit limit; 
    size_t stack_size = 0; 
    int rc = getrlimit(RLIMIT_STACK, &limit); 
    if (rc == 0) { 
     if (limit.rlim_cur != 0LL) { 
      stack_size = (size_t)limit.rlim_cur; 
     } 
    } 


    pthread_attr_t thread_attr; 
    pthread_attr_init(&thread_attr); 
    pthread_attr_setscope(&thread_attr, PTHREAD_SCOPE_SYSTEM); 
    pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); 
    if (stack_size > 0) { 
     pthread_attr_setstacksize(&thread_attr, stack_size); 
    } 


    /* Start the thread that we will start the JVM on. */ 
    pthread_create(&vmthread, &thread_attr, startJava, (void *)&thread_data_struct); 
    pthread_attr_destroy(&thread_attr); 

    pthread_exit(NULL); 

    return 0; 
} 

線程函數:

void *startJava(void *jvm_lib) 
{ 

    JavaVMInitArgs args; 

    const char* classpath = getenv("CLASSPATH"); 

    // determine classpath 
    char* classpath_opt = str_printf("-Djava.class.path=%s", classpath); 

    JavaVMOption* option = malloc(sizeof(JavaVMOption) * 2); 
    option[0].optionString = classpath_opt; 
    option[1].optionString = str_printf("-verbose:jni");  

    args.version = JNI_VERSION_1_6; 
    args.nOptions = 2; 
    args.options = option; 
    args.ignoreUnrecognized = JNI_FALSE; // don't ignore unrecognized options 

    fptr_JNI_CreateJavaVM JNI_CreateJavaVM_fp = (fptr_JNI_CreateJavaVM)dl_dlsym(jvm_lib, 
      "JNI_CreateJavaVM"); 

    int result = JNI_CreateJavaVM_fp(&jvm, (void**) &env, &args); 
    free(option); 
    free(classpath_opt); 

    // launch java code 
    jclass init_class = (*env)->FindClass(env, "org/classes/Loader"); 

    jmethodID load_id = (*env)->GetStaticMethodID(env, init_class, "Load", 
     "(Ljava/lang/String;Lorg/classes/stuff;J)V"); 

    (*env)->CallStaticVoidMethod(env, init_class, load_id); 
} 

Java代碼:(修訂版)

package org.classes; 

import java.awt.AWTException; 
import java.awt.Component; 
import java.awt.Frame; 
import java.awt.image.BufferedImage; 
import java.awt.EventQueue; 

public class Loader { 
    public static void Load(String baseDir, Stuff stuff, long nativePointer) 
    { 
     EventQueue.invokeLater(new Runnable() { 
     public void run() { 
       System.loadLibrary("drawingHelperLibrary"); 

       ... 
       ... 
       ... 

       // start test window 
       Frame frame = new Frame(); 
       frame.setSize(640,480); 
       frame.setLocation(50, 50); 
       frame.setVisible(true); 

       } 
     }); 
    } 
} 

所有的O f除了創建導致死鎖或類似情況的窗口之外,上述代碼執行成功,因爲終端保持繁忙狀態,沒有任何CPU使用情況,並且兩個線程都保持活動狀態。

如果我註釋掉有關創建窗口的行,應用程序將成功執行並退出。

這是從jstack輸出:

Full thread dump Java HotSpot(TM) 64-Bit Server VM (20.4-b02-402 mixed mode): 

"Attach Listener" daemon prio=9 tid=1040b1800 nid=0x11b888000 waiting on condition [00000000] 
    java.lang.Thread.State: RUNNABLE 

"Low Memory Detector" daemon prio=5 tid=103806000 nid=0x10b137000 runnable [00000000] 
    java.lang.Thread.State: RUNNABLE 

"C2 CompilerThread1" daemon prio=9 tid=103805800 nid=0x10b034000 waiting on condition [00000000] 
    java.lang.Thread.State: RUNNABLE 

"C2 CompilerThread0" daemon prio=9 tid=103804800 nid=0x10af31000 waiting on condition [00000000] 
    java.lang.Thread.State: RUNNABLE 

"Signal Dispatcher" daemon prio=9 tid=103804000 nid=0x10ae2e000 runnable [00000000] 
    java.lang.Thread.State: RUNNABLE 

"Surrogate Locker Thread (Concurrent GC)" daemon prio=5 tid=103803000 nid=0x10ad2b000 waiting on condition [00000000] 
    java.lang.Thread.State: RUNNABLE 

"Finalizer" daemon prio=8 tid=10409b800 nid=0x10ac28000 in Object.wait() [10ac27000] 
    java.lang.Thread.State: WAITING (on object monitor) 
    at java.lang.Object.wait(Native Method) 
    - waiting on <7f3001300> (a java.lang.ref.ReferenceQueue$Lock) 
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118) 
    - locked <7f3001300> (a java.lang.ref.ReferenceQueue$Lock) 
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:134) 
    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159) 

"Reference Handler" daemon prio=10 tid=10409b000 nid=0x10ab25000 in Object.wait() [10ab24000] 
    java.lang.Thread.State: WAITING (on object monitor) 
    at java.lang.Object.wait(Native Method) 
    - waiting on <7f30011d8> (a java.lang.ref.Reference$Lock) 
    at java.lang.Object.wait(Object.java:485) 
    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116) 
    - locked <7f30011d8> (a java.lang.ref.Reference$Lock) 

"main" prio=5 tid=104000800 nid=0x10048d000 runnable [10048a000] 
    java.lang.Thread.State: RUNNABLE 
    at java.lang.ClassLoader$NativeLibrary.load(Native Method) 
    at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1827) 
    - locked <7f30010a8> (a java.util.Vector) 
    - locked <7f3001100> (a java.util.Vector) 
    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1724) 
    at java.lang.Runtime.loadLibrary0(Runtime.java:823) 
    - locked <7f3004e90> (a java.lang.Runtime) 
    at java.lang.System.loadLibrary(System.java:1045) 
    at sun.security.action.LoadLibraryAction.run(LoadLibraryAction.java:50) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at sun.awt.NativeLibLoader.loadLibraries(NativeLibLoader.java:38) 
    at sun.awt.DebugHelper.<clinit>(DebugHelper.java:29) 
    at java.awt.Component.<clinit>(Component.java:566) 
    at org.classes.Loader.Load(Loader.java:69) 

"VM Thread" prio=9 tid=104096000 nid=0x10aa22000 runnable 

"Gang worker#0 (Parallel GC Threads)" prio=9 tid=104002000 nid=0x103504000 runnable 

"Gang worker#1 (Parallel GC Threads)" prio=9 tid=104002800 nid=0x103607000 runnable 

"Concurrent Mark-Sweep GC Thread" prio=9 tid=10404d000 nid=0x10a6f0000 runnable 
"VM Periodic Task Thread" prio=10 tid=103817800 nid=0x10b23a000 waiting on condition 

"Exception Catcher Thread" prio=10 tid=104001800 nid=0x103401000 runnable 
JNI global references: 913 

我真的不知道還有什麼可怎麼辦。也許這是一個愚蠢的錯誤,但由於這是我第一次看這個Java-C混合,所以我不夠熟練。

更新:我已經更新了java代碼(感謝trashgod),但它仍然無法正常工作。 我錯過了什麼嗎?

回答

7

我能夠通過查看Eclipse項目如何創建其啓動程序來解決此問題。您需要爲JVM生成單獨的線程,但主要方法需要啓動CFRunLoop。

有可能是您的特定實現一些額外的細節,但與此類似,目前在我們的案子:

... 
#include <CoreServices/CoreServices.h> 

static void dummyCallback(void * info) {} 
... 

... 
if (stack_size > 0) { 
    pthread_attr_setstacksize(&thread_attr, stack_size); 
} 

CFRunLoopRef loopRef = CFRunLoopGetCurrent(); 

/* Start the thread that we will start the JVM on. */ 
pthread_create(&vmthread, &thread_attr, startJava, (void *)&thread_data_struct); 
pthread_attr_destroy(&thread_attr); 

CFRunLoopSourceContext sourceContext = { 
    .version = 0, .info = NULL, .retain = NULL, .release = NULL, 
    .copyDescription = NULL, .equal = NULL, .hash = NULL, 
    .schedule = NULL, .cancel = NULL, .perform = &dummyCallback }; 

CFRunLoopSourceRef sourceRef = CFRunLoopSourceCreate(NULL, 0, &sourceContext); 
CFRunLoopAddSource(loopRef, sourceRef, kCFRunLoopCommonModes);   
CFRunLoopRun(); 
CFRelease(sourceRef); 
... 

你可以看一下通過Eclipse執行此:

http://git.eclipse.org/c/equinox/rt.equinox.framework.git

+0

感謝提示,但鏈接選項對應的是什麼? – 2013-12-27 08:16:20

+0

好的,謝謝,我自己找到了:'-framework CoreFoundation' – 2013-12-27 09:19:20

2

繼此example之後,除非使用Cocoa,否則在C端不需要單獨的線程。您需要使用invokeLater()event dispatch thread上構建您的Java GUI,您需要

+0

如果我不創建分離線程,則會出現以下錯誤:_Apple AWT Java VM在第一個線程上加載 - 無法啓動AWT。 由於Java是在第一個線程上啓動的,因此無法啓動AWT。確保應用程序的Info.plist_中沒有指定StartOnFirstThread。關於事件派發線程,我已經在昨天實現了它,但沒有成功。也許我犯了一些錯誤..我會進一步閱讀你發佈的鏈接,我將發佈修改後的代碼。非常感謝您的回答。 – Andrea3000 2012-01-06 07:47:01

+0

我已經更新了添加invokeLater()的java代碼,但沒有任何更改。我是否用錯誤的方式實現了它? – Andrea3000 2012-01-07 14:11:28

+0

不是我能看到的。我重新運行了我的測試,並且出現類似的錯誤:「Apple AWT Java VM在第一個線程上加載 - 無法啓動AWT。不幸的是,我沒有看到有用的[解決方法](http:// www .google.com/search?q = Apple + AWT + Java + VM +已加載+ on + first + thread + - + can%27t + start + AWT)任何可以啓動GUI和_then_加載C庫? – trashgod 2012-01-07 15:04:00

2

我有同樣的問題,如果我在AWT之前加載我的本機庫,然後它掛起。解決方案是在加載我的本機庫之前加載AWT本機庫。

ColorModel.getRGBdefault(); //static code in ColorModel loads AWT native library 
System.loadLibrary("MyLibrary"); //then load your native code 
0

這實際上並不能解決原始海報的問題,但我在嘗試解決類似問題時找到了他/她的帖子。在我的情況下,我需要啓動一個C++程序,並讓它調用一個用Java編寫的圖像庫。該庫使用了一些awt類,所以我看到了死鎖問題,即使我沒有在Java代碼中創建UI。

另外,我想在不同的平臺上編譯相同的C++代碼,所以避免使用Cocoa。

因爲我不需要創建Java UI,所以當它從C++代碼啓動時,它爲我添加「-Djava.awt.headless = true」作爲jvm的一個選項。

我想發佈這個,以防其他人在類似的情況下絆倒在這個帖子尋找答案。

+0

你能解釋一下嗎? – 2016-05-11 15:20:19