JNI官方规范中文版——在程序中集成JVM需要注意的JNI特征

我们已经讨论了JNI在写本地代码和向本地应用程序中集成JVM时的特征。本章接下来的部分分介绍其它的JNI特征。

8.1 JNI和线程

JVM可以做到在相同的地址空间内执行多个线程。由于多个线程可能会在同时共享资源,所以,增加了程序的复杂性。

要完全理解本章的东西,你需要对多线程编程比较熟悉,知道怎么样在Java中用多线程访问共享资源。

8.1.1 约束限制

如果你的本地代码要运行在多个线程中,有一些约束条件需要注意,这样的话,才能使得你的本地代码无论被多少个线程同时运行,都不会出现问题。

1、 JNIEnv指针只在它所在的线程中有效,不能跨线程传递和使用。不同线程调用一个本地方法时,传入的JNIEnv指针是不同的。

2、 局部引用只在创建它们的线程中有效,同样不能跨线程传递。但可以把局部引用转化成全局引用来供多线程使用。

8.1.2 监视器的入口和出口

监视器是JAVA平台的基本同步机制。每一个对象都可以和一个监视器绑定:

synchronized (obj) {

     ...                   // synchronized block

 }

本地代码中可以通过调用JNI函数来达到与上述JAVA代码中等效的同步目的。这要用到两个JNI函数:MonitorEnter负责进入同步块,MonitorExit用来函数同步块。

if ((*env)->MonitorEnter(env, obj) != JNI_OK) {

     ... /* error handling */

 }

 ...     /* synchronized block */

 if ((*env)->MonitorExit(env, obj) != JNI_OK) {

     ... /* error handling */

 };

运行上面这段代码时,线程必须先进入obj的监视器,再执行同步块中的代码。MonitorEnter需要传入jobject作为参数。同时,如果另一个线程已经进入了这个与jobject监视器的话,当前线程会阻塞。如果当前线程在不拥有监视器的情况下调用MonitorExit的话,会产生一个错误,并抛出一个IllegalMonitorStateException异常。上面的代码中包含了MonitorEnterMonitorExit这对函数的调用,在这对函数的使用时,我们一定要注意错误检查,因为这对函数有可能执行失败(比如,建立监视器的资源分配不成功等原因)。这对函数可以工作在jclass、jstring、jarray等类型上面,这些类型的共同特征是,都是jobject引用的特殊类型

有一个MonitorEnter方法,一定也要有一个与之对应的MonitorExit方法。尤其是在有错误或者异常需要处理的地方,要尤其小心。

if ((*env)->MonitorEnter(env, obj) != JNI_OK) ...;

 ...

 if ((*env)->ExceptionOccurred(env)) {

     ... /* exception handling */

     /* remember to call MonitorExit here */

     if ((*env)->MonitorExit(env, obj) != JNI_OK) ...;

 }

 ... /* Normal execution path.

 if ((*env)->MonitorExit(env, obj) != JNI_OK) ...;

调用MonitorEnter而不调用MonitorExit的话,很可能会引起死锁。通过上面这段代码和本节开始时的JAVA代码的比较,你一定能发现用JAVA来进行同步要方便的多,所以,尽量用JAVA来做同步吧,把与同步相关的代码都挪到JAVA中去吧。

8.1.3 监视器等待和唤醒

JAVA还提供了其它一些和线程监视器有关的API:Object.wait、Object.notify、Object.notifyAll。因为监视器等待和唤醒操作没有进入和退出操作对时效性要求那么高,所以,没有提供与这些方法相对应的JNI函数。我们可以通过JNI调用JAVA的机制来调用这些方法。

/* 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);

 }

上例中,我们假设Object.wait、Object.notify和Object.notifyAll已经在其它地方计算好并缓存在全局引用里面了。

8.1.4 在任意地方获取JNIEnv指针

前面我们提到了,JNIEnv指针只在当前线程中有效。那么有没有办法可以从本地代码的任意地方获取到JNIEnv指针呢?比如,一个操作系统的回调函数中,本地代码是无法通过传参的方式获取到JNIEnv指针的。

可以通过调用接口(invocation interface)中的AttachCurrentThread方法来获取到当前线程中的JNIEnv指针:

JavaVM *jvm; /* already set */

 

 f()

 {

     JNIEnv *env;

     (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);

     ... /* use env */

 }

一旦当前线程被附加到JVM上,AttachCurrentThread函数就会返回一个属于当前线程的JNIEnv指针。

有许多方式可以获取JavaVM指针。可以在VM创建的时候记录下来,也可以通过JNI_GetCreatedJavaVMs查询被创建的虚拟机,还可以通过调用JNI函数GetJavaVM或者定义JNI_OnLoad句柄接口。与JNIEnv不同的是,JavaVM只要被缓存在全局引用中,是可以被跨线程使用的。

JDK1.2以后提供了一个新调用接口(invocation interface)函数GetEnv,这样,你就可以检查当前线程是否被附加到JVM上,然后返回属于当前线程的JNIEnv指针。如果当前线程已经被附加到VM上的话,GetEnvAttachCurrentThread在功能上是等价的。

这四种情况下你会用到本书: 1、 在Java程序复用以前写过的C/C++代码。 2、 自己实现一个java虚拟机 3、 学习不同语言如何进行协作,尤其是如何实现垃圾回收和多线程。 4、 把一个虚拟机实现整合到用C/C++写的程序。 本书是写给开发者的。JNI在1997年第一次发布,本书总结了SUN工程师和大量开发者两年来积累的经验。 本书介绍了JNI的设计思想,对这种思想的理解是使用JNI的各种特性的基础。 本书有一部分是JAVA2平台上面的JNI特征规范说明。JNI程序员可以把这部分用作一个手册。JVM开发者在实现虚拟机的时候必须遵守这些规范JNI的部分设计思想来源于Netscape的Java Runtime Interface(JRI)。 第一章 简介 JNI是JAVA平台的一个重要特征,使用它我们可以重用以前用C/C++写的大量代码。本书既是一个编程指南也是一个JNI手册。本书共包括三部分: 1、 第二章通过一个简单的例子介绍了JNI。它的对象是对JNI不熟悉的初学者。 2、 3~10章对JNI特征进行了系统的介绍。我们会举大量的例子来说明JNI的各个特征,这些特征都是JNI重要且常用的。 3、 11~13章是关于JNI的技术规范。可以把这两章当作一个手册。 本书尽量去满足各类读者的需要。指南面向初学者,手册面向有经验的人和自己实现JNI规范的人。大部分读者可能是用JNI来写程序的开发者。本书会假设你有JAVA,C/C++基础。 本章的剩余部分介绍了JNI的背景,扮演的角色和JNI的演化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值