C ++ Multithreaded Java JNI Call Method

I have a simple class in Java:

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

      

And in C ++ I am making the following JNI call:

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);
}

      

If a single program calls the static c_call_function () method, that's fine.

But if the multithreaded program calls c_call_function (), it gives me the following message when the env-> CallStaticIntMethod (clazz, mid_dummyTest); line passes:

Access Violation at 0x000000006FC77154 read by 0x0000000000000000

If the program is multithreaded, it uses the same JNIEnv variable. But I also tried to load the same JNIEnv using the AttachCurrentThread method and it gives me the same problem.

What restriction is called when a method is called by multiple threads if I don't create any local references or even delete or modify anything?

    env->CallStaticIntMethod(clazz, mid_dummyTest);

      

+3


source to share


2 answers


I can run similar code (see below) where I have multiple threads accessing the same JVM (macOS). I am using pthread.

Few important things

  • attaching a thread to the JVM (according to the docs, only one thread can use the JVM at a time)
  • pooling streams
  • I think it's a good idea to use a mutex to prevent multiple threads from joining.

main.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;
    }
}

      

Makefile

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

      

After running the code, you will get the following output:

./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

      

+1


source


env should be obtained separately for each stream. You should use AttachCurrentThread () if and only if the thread was created * not started by the JVM. For each thread that issues AttachCurrentThread () , you must call DetachCurrentThread () .

clazz must be retrieved separately for each thread, or you can store a global reference to the result returned by FindClass () .



mid_dummyTest can be stored in a global variable: this id is independent of thread, env and clazz .

+3


source







All Articles