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

C多线程JavaJNI方法调用

狄海
2023-03-14

我有一个简单的类在Java:

public class MyClass 
{
    public static void dummyTest() 
    {
    }
}

在C中,我执行以下JNI调用:

void c_call_function() 
{
    JNIEnv *env ...// the JNIEnv initialization in JNI...
    jclass clazz ...// the class initialization in JNI...

    jmethodID mid_dummyTest = env->GetStaticMethodID(clazz, "dummyTest", "()V");
    env->CallStaticIntMethod(clazz, mid_dummyTest);
}

如果一个程序调用静态方法c_call_function(),则可以。

但是如果一个多线程程序调用c_call_function(),当传递env行时,它会给我以下消息-

访问违规在0x0000006FC77154读取到0x0000000000000000

如果程序是多线程的,则使用相同的JNIEnv变量。但我也尝试通过AttachCurrentThread方法加载相同的JNIEnv,这给了我同样的问题。

只要我不创建任何本地引用,甚至不删除或修改任何内容,多线程调用下面的方法有什么限制?

    env->CallStaticIntMethod(clazz, mid_dummyTest);

共有2个答案

廖琪
2023-03-14

我能够运行类似的代码(看下面),我有多个线程访问同一个JVM(macOS)。我用的是pthon。

一些重要的事情

  • 将线程附加到JVM("JNI接口指针(JNIEnv)仅在当前线程中有效。如果另一个线程需要访问JavaVM,它必须首先调用attachMONtThread()将自己附加到VM并获得JNI接口指针。")
  • 连接线程
  • 我想使用互斥锁也是一个好主意,以防止多个线程被连接

主. c

#include <stdio.h>
#include <jni.h>
#include <pthread.h>

#define NUM_THREADS 6

pthread_mutex_t mutexjvm;
pthread_t threads[NUM_THREADS];

struct JVM {
  JNIEnv *env;
  JavaVM *jvm;
};

void invoke_class(JNIEnv* env);

void *jvmThreads(void* myJvm) {

  struct JVM *myJvmPtr = (struct JVM*) myJvm;
  JavaVM *jvmPtr = myJvmPtr -> jvm;
  JNIEnv *env = myJvmPtr -> env;

  pthread_mutex_lock (&mutexjvm);
  printf("I will call JVM\n");
  (*jvmPtr)->AttachCurrentThread(jvmPtr, (void**) &(env), NULL);
  invoke_class( env );
  (*jvmPtr)->DetachCurrentThread(jvmPtr);
  pthread_mutex_unlock (&mutexjvm);
  pthread_exit(NULL);
}

JNIEnv* create_vm(struct JVM *jvm)
{
    JNIEnv* env;
    JavaVMInitArgs vm_args;
    JavaVMOption options;
    options.optionString = "-Djava.class.path=./";

    vm_args.options = &options;
    vm_args.ignoreUnrecognized = 0;
    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 1;

    int status = JNI_CreateJavaVM(&jvm->jvm, (void**)&env, &vm_args);
    if (status < 0 || !env)
        printf("Error\n");
    return env;
}

void invoke_class(JNIEnv* env)
{
    jclass Main_class;
    jmethodID fun_id;
    Main_class = (*env)->FindClass(env, "Main");
    fun_id = (*env)->GetStaticMethodID(env, Main_class, "fun", "()I");
    (*env)->CallStaticIntMethod(env, Main_class, fun_id);
}

int main(int argc, char **argv)
{
    struct JVM myJvm;
    myJvm.env = create_vm(&myJvm);

    if(myJvm.env == NULL)
        return 1;

    pthread_mutex_init(&mutexjvm, NULL);
    for(int i=0; i<NUM_THREADS; i++){
        pthread_create(&threads[i], NULL, jvmThreads, (void*) &myJvm);
        pthread_join(threads[i], 0);
    }
    (*myJvm.jvm)->DestroyJavaVM(myJvm.jvm);
}

Main.java

public class Main {
    public static void main(String[] args){
        System.out.println("Hello, world");
    }
    public static int fun() {
        System.out.println("From JVM");
        return 0;
    }
}

生成文件

all: Main.class main

Main.class: Main.java
    javac Main.java

main.o: main.c
    llvm-gcc -c main.c \
    -I${JAVA_HOME}/include \
    -I${JAVA_HOME}/include/darwin/ \

main: main.o
    ld -o main -L${JAVA_HOME}/jre/lib/server/ \
        -ljvm \
    -rpath ${JAVA_HOME}/jre/lib/server \
    -L/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk \
    -demangle -dynamic -arch x86_64 \
    -macosx_version_min 10.12.0 \
    -lSystem \
    -rpath ${JAVA_HOME}/jre/lib/server/ \
    main.o

clean:
    rm -f Main.class main main.o

运行代码后,将得到以下结果:

./main
I will call JVM
From JVM
I will call JVM
From JVM
I will call JVM
From JVM
I will call JVM
From JVM
I will call JVM
From JVM
I will call JVM
From JVM
牛嘉谊
2023-03-14

每个Thread应单独获得env。当且仅当某个线程*不是由JVM启动时,才应使用AttachCurrentThread()。对于发出AttachCurrentThread()的每个线程,必须调用DetachCurrentThread()。

Clazz应该为每个线程单独获取,或者您可以保存对从FindClass()接收的结果的全局引用。

mid_dummyTest可以保存在全局变量中:该id独立于线程、env和clazz。

 类似资料:
  • 我想跨调用缓存方法ID。为此,我得到方法ID: 我的问题是:我是否需要使用NewGlobalRef创建全局引用,或者可以从本地引用获取并保存方法ID(我使用FindClass获取class\u HelloWorld),而不需要全局引用类元数据?

  • 我需要在Springboot中实现多线程,同时使用POST方法调用API。我根据一个SELECT查询从oracle数据库中提取记录,然后使用行映射器逐个遍历每个记录。在下一步中,我只调用一个方法将这些记录发送到API,以postmapping的形式发送这些记录并取回记录。 因为select查询一次可以返回10、20或100条记录。逐个调用每条记录并不理想。我在想我是否可以一次发送多个记录。我不知道

  • 主要内容:线程生命周期,主线程,Thread 类中的属性和方法,创建线程,管理线程,销毁线程多线程就是多个线程同时工作的过程,我们可以将线程看作是程序的执行路径,每个线程都定义了一个独特的控制流,用来完成特定的任务。如果您的应用程序涉及到复杂且耗时的操作,那么使用多线程来执行是非常有益的。使用多线程可以节省 CPU 资源,同时提高应用程序的执行效率,例如现代操作系统对并发编程的实现就用到了多线程。到目前为止我们编写的示例程序都是单线程的应用程序,这样的应用程序一次只能执行一个任务。 线程

  • 多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:基于进程和基于线程。 基于进程的多任务处理是程序的并发执行。 基于线程的多任务处理是同一程序的片段的并发执行。 多线程程序包含可以同时运行的两个或多个部分。这样的程序中的每个部分称为一个线程,每个线程定义了一个单独的执行路径。 本教程假设您使用的是 Linux 操作系统,我们要使用

  • 本文向大家介绍C#多线程学习之(二)操纵一个线程的方法,包括了C#多线程学习之(二)操纵一个线程的方法的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了C#多线程学习之操纵一个线程的方法。分享给大家供大家参考。具体实现方法如下: 下面我们就动手来创建一个线程,使用Thread类创建线程时,只需提供线程入口即可。(线程入口使程序知道该让这个线程干什么事) 在C#中,线程入口是通过ThreadS

  • 本文向大家介绍C#多线程与跨线程访问界面控件的方法,包括了C#多线程与跨线程访问界面控件的方法的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了C#多线程与跨线程访问界面控件的方法。分享给大家供大家参考。具体分析如下: 在编写WinForm访问WebService时,常会遇到因为网络延迟造成界面卡死的现象。启用新线程去访问WebService是一个可行的方法。 典型的,有下面的启动新线程示例