jni和线程
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异常。上面的代码中包含了MonitorEnter和MonitorExit这对函数的调用,在这对函数的使用时,我们一定要注意错误检查,因为这对函数有可能执行失败(比如,建立监视器的资源分配不成功等原因)。这对函数可以工作在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上的话,GetEnv和AttachCurrentThread在功能上是等价的。
jni和线程的更多相关文章
- JNI通过线程c回调java层的函数
1.参看博客:http://www.jianshu.com/p/e576c7e1c403 Android JNI 篇 - JNI回调的三种方法(精华篇) 2.参看博客: JNI层线程回调Java函数关 ...
- 解决JNI native 线程不能正常退出的问题
本人刚涉足学习C++ 安卓 java,遇到这个棘手的问题,多谢博客园作者lknlfy 看了你的博客解决了这个问题,此文转发, 方便日后学习 以下内容转自lknlfy作者博客 传送门:http:// ...
- 【胡思乱想】JNI与线程池的维护
JNI中,C/C++代码里创建的资源不由Java GC处理,故这里的资源必须由C/C++代码明确释放.在JNI中,C/C++回调Java的方法是调用一个CallXXMethod函数来实现的,如果回调的 ...
- 解决安卓JNI native 线程不能正常退出问题二
直面这个解决方法的可以看我转载的博客 https://www.cnblogs.com/Carlsblog/p/9438016.html 本方法是个投机取巧法,不过也解决了不能正常 ...
- Android JNI 启动线程,并设置线程名称
p.p1 { margin: 0; font: 12px Menlo; color: rgba(100, 56, 32, 1); background-color: rgba(255, 255, 25 ...
- jni调试3(线程调试env变量问题)
jni层调试线程死机原因 一,导致死机原因: jni层中 线程函数中 只要添加调用env 的函数 ,,就会死机 二,解决方法 第一我们应该理解: ①(独立性) JNIEnv 是一个与线 ...
- JNI与多线程
在android开发过程中,由于主线程要聚焦于UI交互,为了软件运行流畅必然要用到很多多线程技术.而在JNI机制中专门提供了一些避免线程冲突的函数.了解.学习并掌握如何避免线程冲突问题是一个程序猿的必 ...
- Java层与Jni层的数组传递(转)
源:Java层与Jni层的数组传递 Android开发中,经常会在Java代码与Jni层之间传递数组(byte[]),一个典型的应用是Java层把需要发送给客户端的数据流传递到Jni层,由Jni层的S ...
- JNI详解---从不懂到理解
转载:https://blog.csdn.net/hui12581/article/details/44832651 Chap1:JNI完全手册... 3 Chap2:JNI-百度百科... 11 C ...
随机推荐
- POJ Protecting the Flowers
点击打开题目 题目大意 奶牛要吃花,FJ来赶牛,将第i头牛赶走要2*ti分钟,奶牛每分钟吃di个单位花,求花的最小损失 先赶吃花多的,Wrong Answer QAQ 我们可以算一算损失 设sum=d ...
- Kivy Event 在哪里?
kivy 程序都是都需要继承APP基类 而App类在Lib\site-packages\kivy\app.py文件定义 from kivy.event import EventDispatcher c ...
- keras模型可视化
#keras.utils.vis_utils模块提供了画出Keras模型的函数(keras版本2.0.2以上)pip install graphviz pip install pydotplus im ...
- 【WPF学习】第三十一章 WPF命令模型
WPF命令模型由许多可变的部分组成.总之,它们都具有如下4个重要元素: 命令:命令表示应用程序任务,并且跟踪任务是否能够被执行.然而,命令实际上不包含执行应用程序任务的代码. 命令绑定:每个命令绑定针 ...
- TensorFlow 编程基础
1.TensorFlow 安装:https://www.cnblogs.com/pam-sh/p/12239387.html https://www.cnblogs.com/pam-sh/p/1224 ...
- hashmap 的边遍历边存储
PS: Hashmap 的一边遍历边存储,可解决例如两数之和. 无重复最长子串问题等,代码为cpp格式. 以无重复最长子串为例. class Solution { public: int length ...
- 迭代器中set的使用
今天对迭代器中的set方法进行了一下简单的使用,由于之前使用过list方法,所以将他与list进行了一下对比. list中加入对象时不会进行查重,也就是只要是一个符合的对象就可以加到list中,而对于 ...
- Struts(四)
1.Struts 2提供了非常强大的类型转换功能,提供了多种内置类型转换器,也支持开发自定义类型转换器2.Struts 2框架使用OGNL作为默认的表达式语言 ==================== ...
- Spring(六)核心容器 - 注册单例 Bean 实例、SingletonBeanRegistry 简介
前言 上篇文章我们对注册 Bean 的核心类 BeanDefinitionRegistry 进行了讨论,这里的注册 Bean 是指保存 Bean 的相关信息,也就是将 Bean 定义成 BeanDef ...
- [flask]邮件配置-20171227
环境变量配置: # PowerShell设置环境变量: $env:MAIL_USERNAME = "" $env:MAIL_PASSWORD = "" 国内: ...