Citation address: http://blog.csdn.net/hust_liuX/archive/2006/12/25/1460486.aspx
I have organized the article here, modified some descriptions, and added some important instructions. The modification is as follows:
Problem description:
A Java object uses JNI to call a send () function in the DLL to send messages to the server. A message is returned immediately after the server message arrives, at the same time, the JNI interface pointer jnienv * ENV (Virtual Machine environment pointer) and jobject OBJ are saved in the DLL variables.
After a period of time, the message receiving thread in the DLL receives the message from the server,
And tries to call the method of the previous Java object (equivalent to the Java callback method) to process the message through the saved env and obj. At this time, the program will suddenly exit (crash ).
Solution:
To solve this problem, you must first understand the cause. So what is the cause of the crash?
The JNI document is clearly stated
TheJNIEnv
Pointer, passed as the first argument to every native method, can only be used in the thread with which it is associated. It is wrong to cacheJNIEnv
Interface pointer obtained from one thread, and use that pointer in another thread.
This means that the jnienv pointer cannot be shared directly in multiple threads. The cause of the program crash described above is as follows: the thread in the callback and the thread that previously saved the variable share the jnienv * env pointer and jobject OBJ variable.
As the http://java.sun.com/docs/books/jni/html/other.html#26206 mentioned,
The jnienv * env pointer cannot be shared by multiple threads, but the JavaVM pointer of the Java Virtual Machine is shared by the entire JVM. We can use JavaVM to obtain the jnienv pointer of the current thread.
Therefore, it is called in the first thread:
JavaVM * gs_jvm; <br/> env-> getjavavm (& gs_jvm); // gets the JavaVM pointer. After obtaining the pointer, save the JavaVM. <Br/>
In another thread B, call
Jnienv * env; <br/> gs_jvm-> attachcurrentthread (void **) & ENV, null );
The jobject pointer of the Java object must also be obtained here, because we need to call back the Java method. like the jnienv pointer, The jobject pointer cannot be shared among multiple threads. that is to say, you cannot directly store the jobject pointer in a thread to a global variable and use it in another thread. fortunately, you can use
Gs_object = env-> newglobalref (OBJ); // create a global variable.
To save the passed OBJ (local variable) to gs_object, so that other threads can use this gs_object (global variable) to manipulate this Java object.
The sample code is as follows:
(1) Java code: Test. Java:
Import Java. io. *; <br/> class test implements runnable <br/>{< br/> Public int value = 0; <br/> static {system. loadlibrary ("test") ;}</P> <p> Public native void setenev (); // local method </P> <p> Public static void main (string ARGs []) throws exception <br/>{< br/> test T = new test (); <br/> T. setenev (); // call the local method </P> <p> while (true) <br/>{< br/> thread. sleep (1000); <br/> system. out. println (T. value); <br/>}< br/>
(2) DLL code: Test. cpp:
# Include "test. H "<br/> # include <windows. h> <br/> # include <stdio. h> <br/> static JavaVM * gs_jvm = NULL; <br/> static jobject gs_object = NULL; <br/> static int gs_ I = 10; </P> <p> jniexport void jnicall java_test_setenev (jnienv * ENV, jobject OBJ) <br/>{< br/> env-> getjavavm (& gs_jvm ); // save it to the global variable JVM <br/> // it is not feasible to directly assign the value of OBJ to the global variable in the DLL. You should call the following function: <br/> gs_object = env-> newglobalref (OBJ); </P> <p> handle ht = createthread (null, 0, (lpthread_start_routine) threadfun, 0, null, null); <br/>}</P> <p> void winapi threadfun (pvoid argv) // call back the thread callback method in JNI <br/>{< br/> jnienv * env; <br/> gs_jvm-> attachcurrentthread (void **) & ENV, null); <br/> jclass CLS = env-> getobjectclass (gs_object); <br/> jfieldid fieldptr = env-> getfieldid (CLS, "value ", "I"); </P> <p> while (1) <br/> {<br/> sleep (100 ); <br/> // change the property value of the Java object (callback Java) <br/> env-> setintfield (gs_object, fieldptr, (jint) gs_ I ++ ); <br/>}< br/>
JNi restrictions:
There are certain constraints that you must keep in mind when writing native methods that are to run in a multithreaded environment. by understanding and programming within these constraints, your native methods will execute safely no matter how many threads simultaneously execute a given native method. for example:
- A jnienv pointer is only valid in the Thread associated with it. you must not pass this pointer from one thread to another, or cache and use it in multiple threads. the Java Virtual Machine passes a native method the same jnienv pointer in consecutive invocations from the same thread, but passes different jnienv pointers when invoking that native method from different threads. avoid the common mistake of caching the jnienv pointer of one thread and using the pointer in another thread.
- Local references are valid only in the thread that created them. you must not pass local references from one thread to another. you shoshould always convert local references to global references whenever there is a possibility that multiple threads may use the same reference.