Java Native Interface 五 JNI里的多线程与JNI方法的注册
本文是《The Java Native Interface Programmer’s Guide and Specification》读书笔记
JNI里的多线程
在本地方法里写有关多线程的代码时,需要知道下面几个约束:
- 一个JNIEnv指针只在与它关联的线程里有效,也就是说,在线程间传递JNIEnv指针和在多线程环境里通过缓存来使用它是不允许和不安全的。JVM在同一个线程里多次调用同一个本地方法时传递的是同一个JNIEnv指针,但在不同的线程里调用同一个本地方法时传递的是不同的JNIEnv指针。
- 本地引用只在创建它的线程里有效,也就是说你不能在线程间传递本地引用。因为在多线程的环境里可能会使用到相同的引用,因此我们需要将本地引用转型为全局引用。
JNI里的同步机制(类似于锁的获取与释放)
在本地方法里,可以通过JNI函数来实现Java里的同步块(互斥资源的使用),用方法MonitorEnter来得到一个JNI引用的监控器(锁),方法MonitorExit释放监控器(锁的释放),下面是简单的使用场景:
//省略了其他代码,下面只是本地方法实现代码里的某一部分
if ((*env)->MonitorEnter(env, obj) != JNI_OK) {
... /* 获取obj引用的锁失败,进行相应的处理 */
}
/* 编写需要同步的代码块 */
....
if ((*env)->ExceptionOccurred(env)) {
... /* 异常处理的代码 */
/* 在这里要记得调用 MonitorExit来释放所获得的监控器 */
if ((*env)->MonitorExit(env, obj) != JNI_OK) ...;
}
/*正常的调用MonitorExit来释放锁*/
if ((*env)->MonitorExit(env, obj) != JNI_OK) {
... /* 释放obj引用的锁失败,进行相应的处理 */
};
需要注意的是,在同步代码块里可能会发生异常,我们需要的对应的异常处理代码中调用MonitorExit方法来释放锁,如果忘记调用这个释放监控器的方法,可能会导致死锁的发生。因为在JNI里使用同步机制会比较麻烦,因此我们尽可能在ava的程序里来实现相应的同步机制。
Java API中提供了一些对线程间同步非常有用的方法,如Object.wait,Object.notify,Object.notifyAll来等待获取一个对象的锁,唤醒等待获得对象锁的对象等。但在JNI里并没有提供对应的方法来等待获取对象的监控器,唤醒等待获取对象监控器的对象,因此在JNI里通常采用JNI的方法调用机制来调用对应的Java方法来实现相应的操作。在下面的代码里,我们假设已经获得了相应方法的methodID缓存在全局引用中;
/* 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指针
在首页的介绍中,我们知道JNIEnv指针只在与它相关联的线程里是有效的,一般对于本地方法来说,他们是从JVM中得到这个指针作为方法的第一个参数的。但也有一小部分本地代码不是从JVM中直接得到这个JNIEnv指针的,比如本地方法里的某一部分代码是属于操作系统调用的某一个方法的,因此这可能就导致将JNIEnv指针作为参数是没有用的。这时我们就可以调用方法AttachCurrentThread来得到当前线程的JNIEnv指针。只要当前这个线程已经加载到JVM中,就可以返回正确的指针。
JavaVM *jvm; /* already set */
f()
{
JNIEnv *env;
(*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
... /* use env */
}
上面的代码里,需要先获取一个JVM的指针,JNI里有许多方法可以得到一个JVM指针,JNI_GetCreatedJavaVMs,GetJavaVM等。并且JVM指针是可以保存在全局引用里的。
本地代码的注册
本地代码的注册方式有两种:
- 在执行本地方法前,在Java代码里使用语句
System.loadLibrary("foo")加载本地方法所有的链接库; - 在本地方法的实现里,需要用到其他链接库里的本地方法时,第一种方法就不适用了,比如,在本地方法里声明另一个本地方法
void JNICALL g_impl(JNIEnv *env, jobject self);但它的实现是在另一个链接库里实现的,则需要使用下面的代码来作为这个方法的实现:
//这里某一个本地方法里的代码,省略了其他部分代码
JNINativeMethod nm;
nm.name = "g";//需要使用的其他链接库里的本地方法的名字
/* 方法的描述,如返回值,参数等 */
nm.signature = "()V";
nm.fnPtr = g_impl;//在这里的本地方法的声明的名字
//注册g_impl方法
(*env)->RegisterNatives(env, cls, &nm, 1);
方法g_imlp的声明并不需要遵循JNI的命名规范,因为这只是调用时的方法指针,并不需要展开代码(调用这个方法时,实际调用的是另一个链接库里名为g的JNI方法),所有不需要使用JNIEXPORT,但需要遵循JNI的调用规范。
使用动态注册链接库的方法的好处为:
其他
将本地代码创建的string对象转型为jstring对象返回给Java程序:
jstring JNU_NewStringNative(JNIEnv *env, const char *str)
{
jstring result;
jbyteArray bytes = 0;
int len;
if ((*env)->EnsureLocalCapacity(env, 2) < 0) {
return NULL; /* out of memory error */
}
len = strlen(str);
bytes = (*env)->NewByteArray(env, len);
if (bytes != NULL) {
(*env)->SetByteArrayRegion(env, bytes, 0, len,
(jbyte *)str);
result = (*env)->NewObject(env, Class_java_lang_String,
MID_String_init, bytes);
(*env)->DeleteLocalRef(env, bytes);
return result;
} /* else fall through */
return NULL;
}
将jstring对象转型为本地string对象
char *JNU_GetStringNativeChars(JNIEnv *env, jstring jstr)
{
jbyteArray bytes = 0;
jthrowable exc;
char *result = 0;
if ((*env)->EnsureLocalCapacity(env, 2) < 0) {
return 0; /* out of memory error */
}
bytes = (*env)->CallObjectMethod(env, jstr,
MID_String_getBytes);
exc = (*env)->ExceptionOccurred(env);
if (!exc) {
jint len = (*env)->GetArrayLength(env, bytes);
result = (char *)malloc(len + 1);
if (result == 0) {
JNU_ThrowByName(env, "java/lang/OutOfMemoryError",
0);
(*env)->DeleteLocalRef(env, bytes);
return 0;
}
(*env)->GetByteArrayRegion(env, bytes, 0, len,
(jbyte *)result);
result[len] = 0; /* NULL-terminate */
} else {
(*env)->DeleteLocalRef(env, exc);
}
(*env)->DeleteLocalRef(env, bytes);
return result;
Java Native Interface 五 JNI里的多线程与JNI方法的注册的更多相关文章
- Java Native Interface 六JNI中的异常
本文是<The Java Native Interface Programmer's Guide and Specification>读书笔记 在这里只讨论调用JNI方法可能会出现的异常, ...
- Java Native Interface 四--JNI中引用类型
本文是<The Java Native Interface Programmer's Guide and Specification>读书笔记 JNI支持将类实例和数组类型(如jobjec ...
- Java Native Interface 二 JNI中对Java基本类型和引用类型的处理
本文是<The Java Native Interface Programmer's Guide and Specification>读书笔记 Java编程里会使用到两种类型:基本类型(如 ...
- android 学习随笔二十七(JNI:Java Native Interface,JAVA原生接口 )
JNI(Java Native Interface,JAVA原生接口) 使用JNI可以使Java代码和其他语言写的代码(如C/C++代码)进行交互. 问:为什么要进行交互? 首先,Java语言提供的类 ...
- JNI(Java Native Interface)
一.JNI(Java Native Interface) 1.什么是JNI: JNI(Java Native Interface):java本地开发接口 ...
- Java Native Interface Specification(JNI)
Java Native Interface Specification(JNI) 使用场景: 需要的功能,标准的java不能提供 有了一个用其他的语言写好的工具包,希望用java去访问它 当需要高性能 ...
- 【详解】JNI (Java Native Interface) (四)
案例四:回调实例方法与静态方法 描述:此案例将通过Java调用的C语言代码回调Java方法. 要想调用实例对象的方法,需要进行以下步骤: 1. 通过对象实例,获取到对象类的引用 => GetO ...
- 【详解】JNI (Java Native Interface) (三)
案例三:C代码访问Java对象的实例变量 获取对象的实例变量的步骤: 1. 通过GetObjectClass()方法获得此对象的类引用 2. 通过类引用的GetFieldID()方法获得实例变量的 ...
- 【详解】JNI (Java Native Interface) (二)
案例二:传递参数给C代码,并从其获取结果 注:这里传递的参数是基本类型的参数,在C代码中有直接的映射类型. 此案例所有生成的所有文件如下: (1)编写案例二的Java代码,如下: 这里我们定义了一个n ...
随机推荐
- git初级--配置
source: http://www.cnblogs.com/sakurayeah/p/5800424.html 一.注册github账号 github网址https://github.com/ 下一 ...
- Node中的定时器详解
在大多数的业务中,我们都会有一些需求,例如几秒钟实现网页的跳转,几分钟对于后台数据进行清理,node与javascript都具有将代码延迟一段时间的能力.在node中可以使用三种方式实现定时功能:超时 ...
- ubuntu中pycharm安装激活第二种方法的密钥
43B4A73YYJ-eyJsaWNlbnNlSWQiOiI0M0I0QTczWVlKIiwibGljZW5zZWVOYW1lIjoibGFuIHl1IiwiYXNza WduZWVOYW1lIjoi ...
- mvn-打jar运行包(含环境变量配置)
前沿条件 maven下载:http://maven.apache.org/download.cgi 配置环境变量 MAVEN_HOME= D:\Softwares\apache-maven-3.2.2 ...
- mac搭建测试服务器
代码可以参考: https://github.com/BigShow1949/MyServe 这里也有jar包 一.下载一个jar包 点击链接下载服务器端[moco服务端] https://repo ...
- 启动 Eclipse 弹出“Failed to load the JNI shared library jvm.dll”错误的解决方法
原因1:给定目录下jvm.dll不存在. 对策:(1)重新安装jre或者jdk并配置好环境变量.(2)copy一个jvm.dll放在该目录下. 原因2:eclipse的版本与jre或者jdk版本不一致 ...
- python爬乌云dorps文章
有时候翻看drops 无赖drops没有一个全部文章列表,所以就有了这个想法把所有文件标题链接都爬出来这样又直观又好找感兴趣的文章 #coding=utf-8 import re import url ...
- windows常用快捷键
windows常用快捷键 Fn+F1:Windows的支持和帮助 =Windows 徽标+F1 Fn+F2:重命名对象 Fn+F3:查找所有文件 Fn+F4:选择“转到不同的文件夹”框并沿框中的项向下 ...
- 11 Set和Map数据结构
Set和Map数据结构 Set WeakSet Map WeakMap 首先 这四个对象都是 数据结构. 用于存放数据 Set 类似数组. 但是不能重复. 如果你有重复数据,会自动忽略 属性 size ...
- Fixing DSDT
https://clover-wiki.zetam.org/Fixing-DSDT#dsdt-mask_fixdisplay_0100-bit-8