本机线程类函数的JNI流是什么

What is the JNI flow for native Thread class functions?

本文关键字:是什么 JNI 线程 类函数 本机      更新时间:2023-10-16

我是JNI领域的新手,我的目标是研究java.lang.Thread的本机方法,如registerNatives和start0(),以便在操作系统级别了解当Threada) 创建b) 启动

因此,我浏览了JNI的各种教程,以便了解基本知识。看起来有4个主要步骤a) 在类中编写本机方法的声明(如Thread.java中的start0())b) 使用jdk/bin中的javah工具生成.h文件c) 将这些生成的.h文件和jdk/include中的其他常见.h文件包含到c/c++项目环境中d) 编写所需的c++函数。

如果我漏了一步,请纠正我。

问题-1) 当我将步骤b)应用于像Thread.java这样的标准java文件时,我在JDK中找不到像Thread.h这样的东西,而且它是源代码。相同的确切位置应该是什么?我在apache harmony中得到了Thread.h的链接http://svn.apache.org/repos/asf/harmony/enhanced/sandbox/bootjvm/bootJVM/jni/src/harmony/generic/0.0/include/java_lang_Thread.h这和我期望在jdk中的文件完全一样。2) 我在jdk\src\share\native\java中看到一个名为Thread.C的文件,该文件包括我在第1点中期望的文件,java_lang_Thread.h。Thread.C文件包含代码

Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)

{(*env)->RegisterNatives(env,cls,methods,ARRAY_LENGTH(methods));}理想情况下,我希望这个代码在Thread.h中,为什么它在Thread.C中,它的意义是什么?3) openjdk\hotspot\src\share\vm\runtime中有一个名为thread.cpp的文件,它包含所有方法定义,现在从thread.h 开始

JNIEXPORT void JNICALL Java_java_lang_Thread_start(JNIEnv *, jobject);

我们如何将其映射到Thread.start,因为我没有注意到start0()到start()之间的映射。4) 由于我是一名java程序员,可能很难理解Thread.CPP中的c++代码,有人能引导我找到一个链接,其中可能包含这些方法的理论,如set_stack_base(NULL)等吗?

我没有到jdk或java线程源的链接。也许如果你提供一个,我可以解决你的问题。

然而,我从你的问题中收集到的是:"Thread.h如何链接到Thread.cpp?"如果这是你要问的问题,那么可以这样想:

h只包含一堆声明。类似于Java接口。线程.c包含该接口的实现。

至于我对你所问的问题的第二个猜测:"java是如何创建本机线程的?"。

如果我必须对Java在Windows上创建线程进行大量猜测,我会说定义要么是使用WINAPI(仅限Windows)编写的,要么是使用C++stl(可移植):

假设您有一个java线程类:

public class Threading {
static{ System.LoadLibrary("Threading"); }
private Runnable r = null;
private native void StartNativeThread(Runnable r);
public Threading(Runnable r) {
this.r = r;
}
public void start() {
StartNativeThread(this.r);
}
}

上面的类在其构造函数中传递了一个可运行的。当您调用start时,它会调用本机函数"StartNativeThread",该函数将可运行的作为参数。在C++端,它将创建一个线程,该线程将调用从java端获得的Runnable.run。

WINAPI-C++:

//object that will hold all thread information.
struct ThreadParams
{
JNIEnv* env;
jobject runnable;
};
//function that the thread will call.
DWORD __stdcall RunnableThreadProc(void* ptr)
{
ThreadParams* p = reinterpret_cast<ThreadParams*>(ptr); //get our thread info from the parameter.
JNIEnv* env = p->env; //grab our env.
jobject runnable = p->runnable; //grab our runnable object.
delete p; //since we allocated on the heap using new, we must delete from the heap.
//this is because c++ does not have garbage collection.
jclass RunnableInterface = env->GetObjectClass(runnable); //get our java runnable interface instance.
jmethodID Run = env->GetMethodID(RunnableInterface, "run","()V"); //get the run method function pointer.
env->CallObjectMethod(RunnableInterface, Run); //call RunnableInterface.run();
}
JNIEXPORT void JNICALL Java_JNIExample_StartNativeThread(JNIEnv* env, jobject obj, jobject runnable)
{
ThreadParams* ptr = new ThreadParams(); //create an object to store our parameters.
ptr->env = env;  //store the env parameter.
ptr->runnable = runnable; //store the runnable object.
//create a thread that calls "RunnableThreadProc" and passes it "ptr" as a param.
CreateThread(0, 0, RunnableThreadProc, reinterpret_cast<void*>(ptr), 0, 0); 
}

老实说,上面看起来很复杂,但这就是WINAPI。它是一个用C语言为windows编写的API。

如果您有一个C++x11编译器,并且希望避免使用winapi并使用STL-C++,这可以在几行中完成。假设我们有与上面相同的java类,那么我们的函数变成:

JNIEXPORT void JNICALL Java_JNIExample_StartNativeThread(JNIEnv* env, jobject obj, jobject runnable)
{
std::thread([&]{
jclass RunnableInterface = env->GetObjectClass(runnable);
jmethodID Run = env->GetMethodID(RunnableInterface, "run","()V");
env->CallObjectMethod(RunnableInterface, Run);
}).detach();
}

注意,[&]{....}是Lambda函数。它意味着可以在另一个函数或参数内部创建的函数。

上述内容也可以翻译为:

void ThreadProc(JNIEnv* env, jobject runnable)
{
jclass RunnableInterface = env->GetObjectClass(runnable);
jmethodID Run = env->GetMethodID(RunnableInterface, "run","()V");
env->CallObjectMethod(RunnableInterface, Run);
}
JNIEXPORT void JNICALL Java_JNIExample_StartNativeThread(JNIEnv* env, jobject obj, jobject runnable)
{
std::thread(ThreadProc, env, obj).detach();
}

现在,实现诸如停止和暂停之类的其他事情也同样容易。您只需在可运行程序的java端执行此操作。或者,您可以使用WINAPI的TerminateThreadWaitObject等在C++端执行此操作。或者,如果您选择使用STL-C++,那么您将使用std::condition_variable

我希望这能澄清一些事情。如果你有任何进一步的问题,你可以发表评论或发一个新的帖子。由你决定。否则,如果我遗漏了什么或把你的问题解释错了,请澄清。


编辑所以对于实际的实现,我们可以看到Thread.c包括jvm.h。因此,我们必须找到jvm.h和jvm.cpp.

快速搜索会出现:

线程.h:http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/00cd9dc3c2b5/src/share/native/java/lang/Thread.c

JVM.h:http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/tip/src/share/vm/prims/jvm.h

JVM.cpp:http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/prims/jvm.cpp

如果我们现在从thread.c中搜索任何函数。我们可以在thread.c上看到start0被映射到JVM_StartThread,所有其他线程函数都被映射到JVM XXXXSomeThreadFunc。。。

我们现在必须在JVM.h和JVM.cpp中查找这些JVM_函数。一旦找到,您就可以了解这些函数是如何完成的。