ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
假定你又一个多线程应用程序,比如C实现的Web服务器。Http请求道来的时候,服务器创建了一定数目的Native线程用于并发的处理这些Http请求。我们想要在这个Wev服务器中嵌入一个Java虚拟机,这样多线程就可以同时在Java虚拟机中执行任务了。处理流程如下图所示: ![](https://box.kancloud.cn/2016-05-25_5745a067b07ee.png) 服务器衍生出的Native方法的生命周期可能比Java虚拟机中的线程要短。因此,因此我们需要将一个Native线程绑定到Java已经运行的虚拟机线程上,从这个Native线程中发起JNI调用,然后在不影响其他已绑定线程的前提下从虚拟机中解除绑定。 以下示例,attach.c,展示了如何使用Invocation interface将Native线程绑定到虚拟机。 >原书示例是用Win32 API写的,我这里的代码是在ubuntu上重写的,将thread相关的API换了而已。 ~~~ #include <pthread.h> #include <jni.h> JavaVM * jvm; #define PATH_SEPARATOR ';' #define USER_CLASSPATH "." void* thread_fun(void *arg){ jint res; jclass cls; jmethodID mid; jstring jstr; jobjectArray args; jclass stringClass; JNIEnv* env; char buf[100]; int threadNum = (int)arg; /* Pass NULL as the third argument */ #ifdef JNI_VERSION_1_2 res = (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL); #else res = (*jvm)->AttachCurrentThread(jvm, &env, NULL); #endif if(res < 0) { fprintf(stderr, "Attach failed\n"); return; } cls = (*env)->FindClass(env, "Prog"); if(!cls){ goto detach; } mid = (*env)->GetStaticMethodID(env,cls, "main", "([Ljava/lang/String;)V"); if(!mid){ goto detach; } sprintf(buf, " from Thread %d", threadNum); jstr = (*env)->NewStringUTF(env, buf); if(!jstr){ goto detach; } stringClass = (*env)->FindClass(env, "java/lang/String"); args = (*env)->NewObjectArray(env, 1, stringClass, jstr); if(!args){ goto detach; } (*env)->CallStaticVoidMethod(env,cls,mid,args); detach: if((*env)->ExceptionOccurred(env)) { (*env)->ExceptionDescribe(env); } (*jvm)->DetachCurrentThread(jvm); } int main(){ JNIEnv *env; int i; jint res; #ifdef JNI_VERSION_1_2 JavaVMInitArgs vm_args; JavaVMOption options[1]; options[0].optionString = "-Djava.class.path=" USER_CLASSPATH; vm_args.version = 0x00010002; vm_args.options = options; vm_args.nOptions = 1; vm_args.ignoreUnrecognized = JNI_TRUE; /* Create the Java VM */ res = JNI_CreateJavaVM(&jvm, (void**)&env, (void*)&vm_args); #else JDK1_1InitArgs vm_args; char classpath[1024]; vm_args.version = 0x00010001; JNI_GetDefaultJavaVMInitArgs(&vm_args); /* Append USER_CLASSPATH to the default system class path */ sprintf(classpath, "%s%c%s", vm_args.classpath, PATH_SEPERATOR, USER_CLASSPATH); vm_args.classpath = classpath; /* Create the Java VM */ res = JNI_CreateJavaVM(&jvm, &env, &vm_args); #endif /* JNI_VERSION_1_2 */ if(res <0){ fprintf(stderr, "Can't create java VM\n"); return 1; } for(i = 0; i < 5; ++i){ pthread_t thread; pthread_create(&thread, NULL, thread_fun, (void*)i); } sleep(1); (*jvm)->DestroyJavaVM(jvm); } ~~~ 以上的attach示例是在invoke.c上修改未来的,与invoke.c不同的是,它没有从主线程调用Prog.main,而是从Native代码启动的五个线程中调用的。在创建并启动子线程之后程序等待他们完成并调用DestroyJavaVM.每一个子线程会将自己绑定到Java虚拟机上并调用Prog.main函数,然后将自己从Java虚拟机上解除绑定。 JNI_AttachCurrentThread的第三个参数是NULL,Java 2 SDK 1.2加入了JNI_ThreadAttachArgs结构,允许你指定额外的参数,比如你想要绑定的线程组。 当程序执行函数DetachCurrentThread的时候,会释放所有该线程相关的所有局部引用。 执行程序将会产生以下输出: ~~~ Hello World from Thread 4 Hello World from Thread 1 Hello World from Thread 3 Hello World from Thread 2 Hello World from Thread 0 ~~~ 当然了,真实的输出次序可能根据实际情况有所不同。