当前位置: 首页 > 知识库问答 >
问题:

缓存JNI对象和线程安全(在Android中)

康文昌
2023-03-14

我正在用本机线程(pthreads)编写一个C应用程序,我需要调用一些Java方法等。我不确定哪些JNI对象可以安全地缓存在我的C对象中,以便以后使用,可能/可能由另一个线程使用。我确实知道,如果我的类的方法可以被不同的线程调用,我不能缓存JNIEnv,而是缓存JavaVM并通过附加当前线程获得JNIEnv。但这是否也意味着我无法缓存从JNIEnv获得的任何内容?我需要使用通过以下JNIEnv方法获得的对象:

FindClass、GetMethodID、NewObject、NewGlobalRef

这些是在线程之间保持有效,还是每次都必须获得新的线程?如果是后者,有没有办法在一个本机线程中创建一个对象,并能够在不同的线程中访问同一个对象?

共有2个答案

钱京
2023-03-14

对象不是特定于线程的。它们最初是“本地”引用,如果你想保留一个副本,你必须通过创建(并最终删除)一个“全局”引用来告诉虚拟机你正在这样做。

看见http://developer.android.com/training/articles/perf-jni.html,尤其是“局部和全局参考”部分。

柏正平
2023-03-14

JNI方法,如FindClass、GetMethodID、GetFieldID等,都是昂贵的操作,保证在JVM的生命周期内生成相同的结果。由于这些操作非常耗时,因此明智的做法是将结果存储在某个地方,以便稍后在本机端重用(这是缓存)。

JNI缓存只考虑这些JNI函数调用。如果您想缓存任何其他C或Java对象,这是另一个主题。(我只是想澄清一下)。

缓存的类、方法和字段不依赖于从中检索它们的线程,因此它们在不同的线程中有效。在使用Set获取或设置某个对象的字段时,最多只能执行线程安全操作

由于FindClass返回对类对象的局部引用,因此必须将其转换为全局引用,以确保在检索该类对象的函数结束后重用该类对象。您可以通过使用NewGlobalReference来实现这一点:

jclass tmp_double_Class = env->FindClass( "java/lang/Double" ); // Check for exceptions!
double_Class = static_cast<jclass>( env->NewGlobalRef( tmp_double_Class ) );
if( double_Class == NULL )
  return;
env->DeleteLocalRef( tmp_double_Class );

这里有一个关于所有JNI缓存主题的示例:

MyJni。cpp:

// Just a shortcut for checking for exceptions
#define CHECK_JNI_EXCEPTION( JNIenv ) \
  if( JNIenv->ExceptionCheck() )\
  {\
    JNIenv->ExceptionClear();\
    return JNI_FALSE;\
  }\
\

// Global variables
jclass    point_Class;
jmethodID point_ctor_Method;
jfieldID  point_x_Field;
jfieldID  point_y_Field;

JNIEXPORT jboolean JNICALL Java_com_company_package_MyClass_nativeInit( JNIEnv * env,
                                                                        jclass   clazz )
{
  // Cache java.lang.Double class, methods and fields
  jclass tmp_point_Class = env->FindClass( "android/graphics/Point" );
  CHECK_JNI_EXCEPTION( env )
  point_Class = static_cast<jclass>( env->NewGlobalRef( tmp_point_Class ) );
  if( point_Class == NULL )
    return JNI_FALSE;
  env->DeleteLocalRef( tmp_point_Class );
  point_ctor_Method = env->GetMethodID( point_Class, "<init>", "(II)V" );
  CHECK_JNI_EXCEPTION( env )
  point_x_Field = env->GetFieldID( point_Class, "x", "I" );
  CHECK_JNI_EXCEPTION( env )
  point_y_Field = env->GetFieldID( point_Class, "y", "I" );
  CHECK_JNI_EXCEPTION( env )
  return JNI_TRUE;
}

MyJni。java:

package com.company.package;

class MyClass {
  // ... All java code here ... 

  // Trigger JNI Caching (could be also done using JNI_OnLoad...)
  private static native void nativeInit();

  static {
    System.loadLibrary( "mylib" );
    nativeInit(); // should check the result
  }
}

有乐趣;)

 类似资料:
  • 问题内容: 假设我们的应用程序中有一个CountryList对象,该对象应返回国家/地区列表。国家/地区的加载是一项繁重的操作,因此应将列表缓存。 其他要求: CountryList应该是线程安全的 CountryList应该延迟加载(仅按需加载) CountryList应该支持缓存无效 考虑到极少数情况下会使缓存无效,应该优化CountryList 我想出了以下解决方案: 你怎么看待这件事?你看

  • 问题内容: 寻找一个适用于Android(SDK 7+)类的简单开源非copyleft缓存。 目的主要是异步存储所获取的s(因此,我不需要将此功能包含在缓存类中)。 我为此目的使用了weakList,这自然是一个糟糕的解决方案,使用Guava缓存会更好一些,但仍然不够好。 最好是,缓存能够存储任何可序列化的对象,而不仅仅是a ,并且当对象添加到缓存中时,我可以轻松清除对象中使用的某些标记。 最好的

  • 问题内容: Spring对象是线程安全的吗?如果没有,如何使它们线程安全? 问题答案: 这是两个不相关的问题: spring线程安全吗? 没有。 Spring具有不同的bean 作用域(例如Prototype,Singleton等),但是所有这些作用域都是在创建bean 时强制执行的。例如,每次“注入”一个“原型”范围的bean都会被创建,而一个“单个”范围的bean将被创建一次并在应用程序上下文

  • 本文向大家介绍高吞吐、线程安全的LRU缓存详解,包括了高吞吐、线程安全的LRU缓存详解的使用技巧和注意事项,需要的朋友参考一下 本文研究的主要是高吞吐、线程安全的LRU缓存的相关内容,具体介绍如下。 几年以前,我实现了一个LRU缓存用来为关键字来查找它的id。数据结构非常有意思,因为要求的吞吐很大足以消除大量使用locks和synchronized关键字带来的性能问题,应用是用java实现的。 我

  • 问题内容: 阅读“实践中的Java并发性”,第3.5节包含以下内容: 除了创建两个的明显的线程安全隐患外,该书还声称可能会发生发布问题。 此外,对于诸如 一个可以扔! 这怎么可能?我能想到的唯一允许这种荒谬行为的方法是,如果构造函数不被阻塞,那么当构造函数代码仍在另一个线程中运行时,将创建对实例的引用。 这可能吗? 问题答案: 之所以可行,是因为Java的内存模型较弱。它不保证读写顺序。 可以通过

  • 我得到的是它们是线程安全的。 杰里米·曼森博客的片段- 因为this引用存储在lastconstructed中“,因此转义构造函数 请建议。