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和线程的更多相关文章

  1. JNI通过线程c回调java层的函数

    1.参看博客:http://www.jianshu.com/p/e576c7e1c403 Android JNI 篇 - JNI回调的三种方法(精华篇) 2.参看博客: JNI层线程回调Java函数关 ...

  2. 解决JNI native 线程不能正常退出的问题

    本人刚涉足学习C++ 安卓  java,遇到这个棘手的问题,多谢博客园作者lknlfy 看了你的博客解决了这个问题,此文转发, 方便日后学习 以下内容转自lknlfy作者博客  传送门:http:// ...

  3. 【胡思乱想】JNI与线程池的维护

    JNI中,C/C++代码里创建的资源不由Java GC处理,故这里的资源必须由C/C++代码明确释放.在JNI中,C/C++回调Java的方法是调用一个CallXXMethod函数来实现的,如果回调的 ...

  4. 解决安卓JNI native 线程不能正常退出问题二

    直面这个解决方法的可以看我转载的博客            https://www.cnblogs.com/Carlsblog/p/9438016.html 本方法是个投机取巧法,不过也解决了不能正常 ...

  5. Android JNI 启动线程,并设置线程名称

    p.p1 { margin: 0; font: 12px Menlo; color: rgba(100, 56, 32, 1); background-color: rgba(255, 255, 25 ...

  6. jni调试3(线程调试env变量问题)

    jni层调试线程死机原因 一,导致死机原因:   jni层中  线程函数中  只要添加调用env 的函数 ,,就会死机     二,解决方法 第一我们应该理解: ①(独立性) JNIEnv 是一个与线 ...

  7. JNI与多线程

    在android开发过程中,由于主线程要聚焦于UI交互,为了软件运行流畅必然要用到很多多线程技术.而在JNI机制中专门提供了一些避免线程冲突的函数.了解.学习并掌握如何避免线程冲突问题是一个程序猿的必 ...

  8. Java层与Jni层的数组传递(转)

    源:Java层与Jni层的数组传递 Android开发中,经常会在Java代码与Jni层之间传递数组(byte[]),一个典型的应用是Java层把需要发送给客户端的数据流传递到Jni层,由Jni层的S ...

  9. JNI详解---从不懂到理解

    转载:https://blog.csdn.net/hui12581/article/details/44832651 Chap1:JNI完全手册... 3 Chap2:JNI-百度百科... 11 C ...

随机推荐

  1. Windows中安装Linux子系统的详细步骤

    早就听说Windows中可以安装Linux子系统,体验了一下,感觉还是不错的,下面直接开始安装和配置步骤吧! 开启Windows中的配置 首先开启开发者模式 打开"所有设置"进入& ...

  2. Photoshop 2020安装教程

    首先来看看新增功能 [Photoshop 2020新增功能] 1.ipad和云文档 2.预设改进 3.新对象选择工具 4.转换行为一致 5.改进的属性面板 6.智能对到图层 7.增强的转换变形. 需要 ...

  3. 动态规划-Dynamic Programming(DP)

    动态规划 动态规划方法心得 ​ 动态规划是一般的面试.笔试中的高频算法题,熟练掌握必要的.动态规划的中心思想是在解决当前问题时,可以由之前已经计算所得的结果并结合现在的限制条件递推出结果.由于此前的计 ...

  4. redis--->事务和锁

    redis 的事务.锁.流水线 Redis与 mysql事务的对比 开启 mysql:start transaction redis:multi 语句:mysql:普通sql redis:普通命令 成 ...

  5. Asp.Net Core 混合全球化与本地化支持

    前言 最近的新型冠状病毒流行让很多人主动在家隔离,希望疫情能快点消退.武汉加油,中国必胜! Asp.Net Core 提供了内置的网站国际化(全球化与本地化)支持,微软还内置了基于 resx 资源字符 ...

  6. android webview正确显示音标

    package com.example.fonttest; import android.support.v7.app.ActionBarActivity; import android.webkit ...

  7. 目标检测之单步检测(Single Shot detectors)

    目标检测之单步检测(Single Shot detectors) 前言 像RCNN,fast RCNN,faster RCNN,这类检测方法都需要先通过一些方法得到候选区域,然后对这些候选区使用高质量 ...

  8. 2019icpc南京网络赛 A The beautiful values of the palace(离线+树状数组)

    题意: (假设所有的点对应的值已经求出)给你一个1e6*1e6的矩阵,有m<=1e5个点有值,其余都为0 q<=1e5个询问,求子矩阵的权值和 思路: 根据二维差分,对于询问左下角(x1, ...

  9. 牛客练习赛34 D little w and Exchange(归纳)

    题意: 给n个数,和m 问这组数是否可以构成[1, m]中的每一个数 思路: 先将a数组排序. 先算算构成前几个数需要什么,至少需要a[1]=1 需要a[2] = 1,2 在a[2] = 1的情况下a ...

  10. lua学习之表达式篇

    表达式 表达式用于表达值 lua 中表达式可以为数字常量,自变字符串,变量,一元和二元操作符,函数调用.函数定义.table 构造式 算数操作符 一元操作符 -负号 二元操作符 -减号 / ^ % x ...