JNI是否具有线程安全功能?

时间:2018-06-17 11:46:17

标签: java multithreading java-native-interface thread-safety openmp

我正在尝试使用OpenMP来并行化将Java字符串数组转换为C字符串数组(char **)的过程:

char** convert(JNIEnv *env, jobjectArray jstringArray, int n_threads){

        int n_rows =(*env)->GetArrayLength(env, jstringArray);
        char **cArray = (char **) malloc(n_rows * sizeof(char*));
        int i=0;
        jstring row;

        if(n_threads <= 0 ) n_threads = 1;
        #pragma omp parallel for num_threads(n_threads) private(i, row)
        for (i=0; i<n_rows; i++) {
                  row = (jstring) (*env)->GetObjectArrayElement(env, jstringArray, i);
                  cArray[i] = (char*)(*env)->GetStringUTFChars(env, row, 0);
                  printf("cArray[%d]: %s thread:%d row:%p env:%p jstringArray:%p\n", i, cArray[i], omp_get_thread_num(), row, env, jstringArray);
        }

        return cArray;
}

但是,在获取jstring行时似乎遇到了竞争状态:

输出n_threads = 1:

cArray[0]: AA thread:0 row:0x7f5b1c000c90 env:0x7f5b300091f8 jstringArray:0x7f5bb3ffdc38
cArray[1]: BB thread:0 row:0x7f5b1c000c98 env:0x7f5b300091f8 jstringArray:0x7f5bb3ffdc38
cArray[2]: CC thread:0 row:0x7f5b1c000ca0 env:0x7f5b300091f8 jstringArray:0x7f5bb3ffdc38
cArray[3]: DD thread:0 row:0x7f5b1c000ca8 env:0x7f5b300091f8 jstringArray:0x7f5bb3ffdc38

n_threads的输出= 3:

cArray[0]: AA thread:0 row:0x7f434c004050 env:0x7f434800a9f8 jstringArray:0x7f435b8f0c48
cArray[1]: BB thread:0 row:0x7f434c004060 env:0x7f434800a9f8 jstringArray:0x7f435b8f0c48
cArray[2]: **CC** thread:1 **row:0x7f434c004058** env:0x7f434800a9f8 jstringArray:0x7f435b8f0c48
cArray[3]: **CC** thread:2 **row:0x7f434c004058** env:0x7f434800a9f8 jstringArray:0x7f435b8f0c48

冲突函数似乎是GetObjectArrayElement(),它返回两个不同线程(示例中为1和2)和2个不同数组索引(2和3)的相同引用。

这种行为是JNI函数固有的,还是我遗漏了什么?

3 个答案:

答案 0 :(得分:3)

一种可能的解决方案是在访问JNI函数之前将每个线程附加到JVM,如下所示:

JavaVM* jvm = NULL;
JNIEnv *t_env;

(*env)->GetJavaVM(env, &jvm);

#pragma omp parallel num_threads(n_threads) private(t_env)
{
  (*jvm)->AttachCurrentThread(jvm, (void**)&t_env, NULL);

  // Call JNI functions here using t_env...

  (*jvm)->DetachCurrentThread(jvm);
}

答案 1 :(得分:2)

我以前从未与JNI合作,但是对谷歌搜索JNI线程安全&#34;进行了初步的搜索。返回this作为第一次点击:

  

JNI接口指针(JNIEnv *)仅在当前线程中有效。您不能将接口指针从一个线程传递到另一个线程,或者缓存接口指针并在多个线程中使用它。 Java虚拟机将在同一个线程的本机方法的连续调用中传递相同的接口指针,但不同的线程将不同的接口指针传递给本机方法。

希望有所帮助。

答案 2 :(得分:2)

您可以拆分#pragma omp parallel ...#pragma omp for ...指令以并行执行操作,但不能循环执行。有关类似问题,请参阅Execute piece of code once per thread in OpenMP without default constructor

这样的事情应该有效(未经测试,所以你可能需要重新编码,因为我怀疑private指令可能位置不正确):

char** convert(JNIEnv *env, jobjectArray jstringArray, int n_threads)
{

    int n_rows =(*env)->GetArrayLength(env, jstringArray);
    char **cArray = (char **) malloc(n_rows * sizeof(char*));
    int i=0;
    jstring row;

    JavaVM* jvm = NULL;
    JNIEnv *thr_env;

    ( *env )->GetJavaVM( env, &jvm );

    if(n_threads <= 0 ) n_threads = 1;
    #pragma omp parallel num_threads(n_threads)  private( thr_env )
    {
        // attach this thread to the JVM
        ( *jvm )->AttachCurrentThread( jvm, ( void** ) &thr_env, NULL );
        // run the for loop
        #pragma omp for private(i, row)
        for (i=0; i<n_rows; i++)
        {
             row = (jstring)
                 (*env)->GetObjectArrayElement(env, jstringArray, i);
             cArray[i] = (char*)(*env)->GetStringUTFChars(env, row, 0);
             printf(
                 "cArray[%d]: %s thread:%d row:%p env:%p jstringArray:%p\n",
                 i, cArray[i], omp_get_thread_num(), row, env, jstringArray);
        }
        ( *jvm )->DetachCurrentThread( jvm );
    }

    return cArray;
}