JNI官方文档翻译7-Java线程和Native线程

        在写这篇文章之前,我还是先简单概括一下第七章的内容:对于java程序而言,native是我们需要访问的库。但是对于一个单纯的c程序而言,我们可以使用一个特殊的c库来创建虚拟机。刚好相反。在C程序里创建一个java虚拟机,然后加载类,找到类里的main方法,然后执行,这样一个虚拟机就跑起来了。事实上,我们在命令行里输入java 命令,其实就是执行类似这样的程序。而java命令是个C程序,负责创建虚拟机的。好了就概括到这里,文档中有个例子,就简单介绍到这。我们开始一个新话题:Java线程和nativie线程。

    


       你需要知道的一些限制:

     1. 在java线程里跑native方法,JNIEnv这个东西只在当前线程有效,千万别把它从一个线程传递到另一个线程使用,也就是说,这个东西不能由多个线程共享。每个java线程都会有一个唯一的JNIEnv与之对应。所以不要试图缓存JNIEnv,这么做容易造成错误。

    2. 局部ref只在当前线程有效,而全局ref没有限制。所以如果你想让局部ref在多线程环境下被其他线程访问,请转成全局ref。



java线程原始的同步方式是这样的:

synchronized (obj) {
... // synchronized block
}

每个对象都是一个monitor(监视器),获得了montor的线程才能执行synchronized block,同一时刻只能有最多一个线程执行synchronized block,其他线程则必须等待当前

扫描二维码关注公众号,回复: 1880100 查看本文章

线程释放monitor才能尝试去获得monitor。

在jni层,我们这么做:

if ((*env)->MonitorEnter(env, obj) != JNI_OK) {
... /* error handling */
}
... /* synchronized block */
if ((*env)->MonitorExit(env, obj) != JNI_OK) {
... /* error handling */
};

obj就是一个jobject,作用和synchronize一样。 确保enter和exit成对执行,如果不调用exit那么可能很可能会死锁。

我们可以看到,在java层声明同步方法还是很简单的,如果可能,尽可能在java层做,而不要在native层写MonitorEnter  MonitorExit。public synchronized native void dowork();


线程同步:native方法没有对应的wait 和notify,所以native方法要想使用线程同步只能调用java层的wait和notify来做:

假设变量已经被正确初始化:

/* precomputed method IDs */
static jmethodID MID_Object_wait;
static jmethodID MID_Object_notify;
static jmethodID MID_Object_notifyAll;
void
JNU_MonitorWait(JNIEnv *env, jobject object, jlong timeout)
{
(*env)->CallVoidMethod(env, object, MID_Object_wait,
timeout);
}
void
JNU_MonitorNotify(JNIEnv *env, jobject object)
{
(*env)->CallVoidMethod(env, object, MID_Object_notify);
}
void
JNU_MonitorNotifyAll(JNIEnv *env, jobject object)
{
(*env)->CallVoidMethod(env, object, MID_Object_notifyAll);
}

前面提过,JNIEnv这个东西不应该被缓存,一个线程对应一个,那么有一些方法如果没有这个参数传过来,我们应该怎么办呢? 假设JavaVM已经被正确初始化:

JavaVM *jvm; /* already set */
f()
{
JNIEnv *env;
(*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
... /* use env */
}

初始化JavaVM的方式有很多,JNI_GetCreatedJavaVMs, GetJavaVM JNI_OnLoad ,值得注意的是,这个JavaVM全局只有一个,可以被缓存起来,做个全局变量。

Java 2 SDK release 1.2提供一种便捷的方法GetEnv获得 JNIEnv,GetEnv 和 AttachCurrentThread作用一样,如果当前线程和虚拟机关联了,就可以返回JNIEnv。




下面对比一下两种线程模型:java线程和native线程

native线程如posix线程,由操作系统调度;  java线程由虚拟机调度。 如果你写jni程序,你一定要注意线程模型。选择java线程,可以跨平台。选择native线程,可能有些操作系统并不很好的支持。两种线程模型混用可能会遇到麻烦,因为你不确定虚拟机层的线程具体实现是什么,可能是和你的native线程用的同一个模型,也可能不是。





猜你喜欢

转载自blog.csdn.net/mtaxot/article/details/51513228
今日推荐