http://www.cnblogs.com/lovingprince/archive/2008/08/19/2166366.html
多线程时,在方法上加 synchronized
我在这里将文章整理了一下,重新修改了部分描述和增加了一些重要的说明事项。修改文如下:
问题描述:
一个java对象通过JNI调用DLL中一个send()函数向服务器发送消息,不等服务器消息到来就立即返回,同时把JNI接口的指针JNIEnv *env(虚拟机环境指针),和jobject obj保存在DLL中的变量里.
一段时间后,DLL中的消息接收线程接收到服务器发来的消息,
并试图通过保存过的env和obj来调用先前的java对象的方法(相当于JAVA回调方法)来处理此消息.此时程序会突然退出(崩溃).
解决办法:
解决此问题首先要明白造成这个问题的原因。那么崩溃的原因是什么呢?
JNI文档上有明确表述
The JNIEnv pointer, passed as the first argument to every native method, can only be used in the thread with which it is associated. It is wrong to cache the JNIEnv interface pointer obtained from one thread, and use that pointer in another thread.
意思就是JNIEnv指针不能直接在多线程中共享使用。上面描述的程序崩溃的原因就在这里:回调时的线程和之前保存变量的线程共享了这个JNIEnv *env指针和jobject obj变量。
在 http://java.sun.com/docs/books/jni/html/other.html#26206 提到,
JNIEnv *env指针不可为多个线程共用,但是java虚拟机的JavaVM指针是整个jvm公用的,我们可以通过JavaVM来得到当前线程的JNIEnv指针.
于是,在第一个线程A中调用:
JavaVM* gs_jvm;
env->GetJavaVM(&gs_jvm); //来获取JavaVM指针.获取了这个指针后,将该JavaVM保存起来。
在另一个线程B里,调用
JNIEnv *env;
gs_jvm->AttachCurrentThread((void **)&env, NULL);
这里还必须获取那个java对象的jobject指针,因为我们要回调JAVA方法.同 JNIEnv 指针一样,jobject指针也不能在多个线程中共享. 就是说,不能直接在保存一个线程中的jobject指针到全局变量中,然后在另外一个线程中使用它.幸运的是,可以用
gs_object=env->NewGlobalRef(obj);//创建一个全局变量
来将传入的obj(局部变量)保存到gs_object中,从而其他线程可以使用这个gs_object(全局变量)来操纵这个java对象了.
示例代码如下:
(1)java代码:Test.java:
import java.io.*;
class Test implements Runnable
{
public int value = 0;
static{ System.loadLibrary("Test");}
public native void setEnev();//本地方法
public static void main(String args[]) throws Exception
{
Test t = new Test();
t.setEnev(); //调用本地方法
while(true)
{
Thread.sleep(1000);
System.out.println(t.value);
}
}
}
(2) DLL代码:Test.cpp:
#include "test.h"
#include<windows.h>
#include<stdio.h>
static JavaVM *gs_jvm=NULL;
static jobject gs_object=NULL;
static int gs_i=10;
JNIEXPORT void JNICALL Java_Test_setEnev(JNIEnv *env, jobject obj)
{
env->GetJavaVM(&gs_jvm); //保存到全局变量中JVM
//直接赋值obj到DLL中的全局变量是不行的,应该调用以下函数:
gs_object=env->NewGlobalRef(obj);
HANDLE ht=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFun,0,NULL,NULL);
}
void WINAPI ThreadFun(PVOID argv)//JNI中线程回调这个方法
{
JNIEnv *env;
gs_jvm->AttachCurrentThread((void **)&env, NULL);
jclass cls = env->GetObjectClass(gs_object);
jfieldID fieldPtr = env->GetFieldID(cls,"value","I");
while(1)
{
Sleep(100);
//这里改变JAVA对象的属性值(回调JAVA)
env->SetIntField(gs_object,fieldPtr,(jint)gs_i++);
}
}
JNI限制:
There are certain constraints that you must keep in mind when writing native methods that are to run in a multithreaded environment. By understanding and programming within these constraints, your native methods will execute safely no matter how many threads simultaneously execute a given native method. For example:
A JNIEnv pointer is only valid in the thread associated with it. You must not pass this pointer from one thread to another, or cache and use it in multiple threads. The Java virtual machine passes a native method the same JNIEnv pointer in consecutive invocations from the same thread, but passes different JNIEnv pointers when invoking that native method from different threads. Avoid the common mistake of caching the JNIEnv pointer of one thread and using the pointer in another thread.
Local references are valid only in the thread that created them. You must not pass local references from one thread to another. You should always convert local references to global references whenever there is a possibility that multiple threads may use the same reference.
分享到:
相关推荐
注意,由于线程安全问题,你需要确保对Java对象的访问是同步的,尤其是在多线程环境中。 5. **编译和链接JNI代码**:使用NDK的`ndk-build`命令(或者Gradle的`externalNativeBuild`配置)编译JNI代码,并将生成的库...
java多线程技术论文—毕业设计论文说明书.
在多线程环境中,每个线程都有自己的执行上下文,这意味着当C++线程回调Java方法时,必须确保Java的同步机制正确地应用,以避免数据竞争和其他并发问题。Java中的`synchronized`关键字和`java.util.concurrent`包下...
### 使用JNA替代JNI调用DLL,并解决内存溢出问题 #### 问题背景 在项目的开发过程中,常常遇到需要处理二进制流数据并对其进行解析处理的情况。这种情况下,如果上层应用平台采用的是Java开发,而底层算法或数据...
- **内存管理**:JNA负责Java和本地代码之间的内存管理,包括自动分配和释放内存,避免了JNI中的内存泄漏问题。 - **异步调用**:JNA支持异步调用,可以用于处理耗时的操作,提高程序的响应性。 - **结构体和...
这几天搞搞JNA,主要是组内小弟靠不住。 找了下资料,居然没有回调函数的样例,郁闷的我不行,于是就自己折腾一晚上,自己实现一个。 C++部分使用ACE实现了一个线程,线程中调用Java部分的回调函数。 C++使用vc8...
因为JNA需要在Java和本地代码之间进行更多的映射和转换。 - 在处理大量或高性能要求的本地调用时,可能需要考虑使用JNI来优化性能。 - 为了确保跨平台兼容性,正确选择和使用`jna-3.3.0-platform.jar`至关重要。 ...
- 多线程支持:JNA支持在多线程环境中安全地调用本地函数。 6. 优化与性能 - 缓存机制:JNA使用内存映射文件和缓存来提高性能,避免重复加载库和解析函数。 - Pointer与Memory管理:理解Pointer和Memory对象的...
5. 并发支持:JNA是线程安全的,可以在多线程环境中安全地使用。 6. 高级特性:JNA还包括回调机制,允许Java方法作为本地库的回调函数;以及内存管理功能,如自定义内存分配和释放策略。 在实际开发中,JNA广泛...
JNA还支持一些高级特性,如回调函数(Java方法作为本地函数的参数)、异步调用、多线程环境下的安全访问等。回调函数允许Java代码作为本地库的一部分被调用,这对于实现复杂的交互逻辑非常有用。异步调用则可以帮助...
除了基本的函数调用,JNA还提供了高级功能,比如自定义类型映射、结构体和联合体的支持、多线程安全以及内存管理等。这些特性使得JNA在处理系统级编程任务时非常灵活和强大。 总的来说,JNA的jar包是一个强大的工具...
8. **线程安全**:JNA在多线程环境中使用时需要注意线程安全问题。例子可能包含如何正确管理和同步对原生资源的访问。 9. **文档和教程**:压缩包可能还包含了一份详细的README文件或教程,解释了如何运行和理解...
而volatile确保了多线程环境中的可见性和有序性,避免了数据不一致的问题。 另外,Java提供了一组高级并发工具,如java.util.concurrent包下的Semaphore(信号量)、CyclicBarrier(循环屏障)、CountDownLatch...
JNA还提供了其他高级功能,如自动类型转换、回调函数支持、多线程安全等。对于复杂的本地库接口,JNA提供了`Function`类和`DynamicLookupStrategy`,使得可以动态查找和调用未在接口中声明的函数。 总的来说,"jna....
它可以帮助开发者理解如何处理各种复杂的本地函数调用情况,包括错误处理、内存管理、多线程安全等问题。 五、库文件(`libs`目录) `libs`目录通常包含JNA库针对不同平台的本地库文件(如`.dll`、`.so`或`.dylib`...
Java Native Access(JNA)是Java平台上的一个开源库,它允许Java代码直接调用操作系统API,无需编写C语言的动态链接库(DLL)或...通过熟练掌握JNA,Java程序员可以更好地融入多平台环境,解决各种复杂的系统级问题。
在JNA 3.1.0版本中,可能还包含了对其他功能的支持,例如回调函数、异步调用、多线程安全等。开发者可以根据自己的需求,查阅JNA的官方文档或相关教程,学习如何充分利用这些特性。 总之,JNA为Java开发者提供了一...
3.2.2和3.5.1是JNA的不同版本,意味着在这两个版本之间,可能增加了新的功能、优化了性能或修复了已知问题。用户可以根据自己的项目需求选择合适的版本进行引用。 JNA_api_3.2.2_htm.rar和JNA_api_3.5.1_htm.zip是...
2. **多线程访问**:JNA支持多线程访问本地库,但在并发调用时应注意同步问题。 #### 知识点七:性能考量 虽然JNA提供了便捷的方式来调用C库,但需要注意的是,频繁的Java/C边界转换可能会带来一定的性能开销。...
它可以处理复杂的数据结构,支持回调函数,甚至能够处理多线程环境下的本地调用。然而,需要注意的是,虽然JNA简化了本地调用,但性能上通常不如直接的JNI,因为JNA有额外的内存管理和类型转换开销。 总的来说,...