使用 JNI、多线程从 Fortran 调用 Java

Calling Java from Fortran using JNI, multithreading

我正在尝试将数组从 Fortran 传递到 Java,在 Java 中进行一些计算,并将值 return 传递给我的 Fortran 程序。我正在使用 JNI 从 Fortran 调用 Java。 我写了一个示例程序,我的代码如下:

main.f95:

PROGRAM MAIN
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER, C_INT, C_DOUBLE
USE array_example
IMPLICIT NONE
INTERFACE
  FUNCTION  obj(c, c_size) RESULT(f) BIND(C, NAME='obj')
    USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_DOUBLE, C_PTR, C_INT
    TYPE(C_PTR), INTENT(IN), VALUE :: c
    INTEGER(C_INT), INTENT(IN):: c_size
    REAL(C_DOUBLE):: f
  END FUNCTION
END INTERFACE

INTEGER :: x, y
x=5

CALL example(obj,x)

END PROGRAM MAIN

mod.f95:

MODULE array_example
CONTAINS
SUBROUTINE example(obj, N)
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER, C_INT, C_DOUBLE, C_LOC
IMPLICIT NONE
INTEGER, INTENT(IN) :: N
REAL(C_DOUBLE) :: res
INTEGER :: j
INTEGER(C_INT) :: flag

INTERFACE
  FUNCTION  obj(c, c_size) RESULT(f) BIND(C, NAME='obj')
    USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_DOUBLE, C_PTR, C_INT
    IMPLICIT NONE
    TYPE(C_PTR), INTENT(IN), VALUE :: c
    INTEGER(C_INT), INTENT(IN):: c_size
    REAL(C_DOUBLE):: f
  END
END INTERFACE

REAL(C_DOUBLE), TARGET :: c_array(1:N)
TYPE(C_PTR) :: cptr

cptr = c_loc(c_array(1))

c_array(1:N)=(/2.6179917, 1.570795, 1.570795, 1.570795, 1.570795/) 

res = obj(cptr, N)

PRINT *, res

END SUBROUTINE
END MODULE

objFuncC.c

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

extern double obj(double *, int *);

JNIEnv* create_vm(JavaVM **jvm)
{
    JNIEnv* env;
    JavaVMInitArgs args;
    JavaVMOption options;
    args.version = JNI_VERSION_1_6;
    args.nOptions = 1;
    options.optionString = "-Djava.class.path=./";
    args.options = &options;
    args.ignoreUnrecognized = 0;
    int rv;
    rv = JNI_CreateJavaVM(jvm, (void**)&env, &args);
    if (rv < 0 || !env)
        printf("Unable to Launch JVM %d\n",rv);
    else
        printf("Launched JVM successfully\n");
    return env;
}


double obj(double *ptr, int *c_size)
{
    JavaVM *jvm;
    JNIEnv *env;
    jdouble *dptr;
    jdoubleArray newArray;
    jint size;
    double result;


    size = *c_size;
    dptr = (double *)ptr;

    env = create_vm(&jvm);
    if(env == NULL)
        return 1;
    newArray = (*env)->NewDoubleArray(env, size);

     (*env)->SetDoubleArrayRegion(env, newArray, 0, size, (const jdouble*)dptr); 

    jclass objFuncJ_class;
    jmethodID main_method;
    jmethodID evaluate_method;

    objFuncJ_class = (*env)->FindClass(env, "objFuncJ"); 
    evaluate_method = (*env)->GetStaticMethodID(env, objFuncJ_class, "evaluate", "([D)D"); 
    result = (*env)->CallStaticDoubleMethod(env, objFuncJ_class, evaluate_method, newArray);

    printf("Result = %f\n",result);
    return result;

}

objFunc.java

import java.io.*;
import java.lang.*;

public class objFuncJ
{
    public static double evaluate(double[] position) 
    {
      int m = 10;
      double sum = 0.0;
      for (int i = 1; i <= position.length; i++) 
      {
        double xi = position[(i - 1)];
        double pow = 1.0;
        double xiPow = Math.sin(i * (xi * xi) / Math.PI);
        for (int j = 1; j <= (2 * m); j++) 
        {
          pow *= xiPow;
        }
        sum += Math.sin(xi) * pow;
        }
        return -sum;
    }
}

我得到以下输出:

Launched JVM successfully
Result = -1.011206
Unable to Launch JVM -5
Unable to Launch JVM -5
Unable to Launch JVM -5
1.0000000000000000   

'Result'中的值是正确的输出。但是,多次执行失败。如何更改我的代码以处理函数的多次评估并仅创建一次 Java JVM?

evaluate_method = (*env)->GetStaticMethodID(env, objFuncJ_class, "evaluate", "([D)D"); 

不同意

public static double evaluate(double[] position, int flg) 

寓意:不要尝试自己编写 JNI 方法签名。使用 javap -s 的输出。永远不会错。

好吧,我知道我错在哪里了。我将 objFuncC.c 更改如下:

extern double obj(double *, int *);
extern int init(); 
static JavaVM *jvm;

JNIEnv* create_vm(JavaVM **jvm)
{
    JNIEnv* env;
    JavaVMInitArgs args;
    JavaVMOption options;
    args.version = JNI_VERSION_1_6;
    args.nOptions = 1;
    options.optionString = "-Djava.class.path=./";
    args.options = &options;
    args.ignoreUnrecognized = 0;
    int rv;
    rv = JNI_CreateJavaVM(jvm, (void**)&env, &args);
    if (rv < 0 || !env)
        printf("Unable to Launch JVM %d\n",rv);
    else
        printf("Launched JVM successfully\n");
    return env;
}

int init() 
{
    JNIEnv *env_init;
    env_init = create_vm(&jvm);
    if(env_init == NULL)
      return 1;
}

double obj(double *ptr, int *c_size)
{
    JNIEnv *env;
    jdouble *dptr;
    jdoubleArray newArray;
    jint size;
    double result;

    size = *c_size;
    dptr = (double *)ptr;

    jint rs = (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
    newArray = (*env)->NewDoubleArray(env, size);

    (*env)->SetDoubleArrayRegion(env, newArray, 0, size, (const jdouble*)dptr); 

    jclass objFuncJ_class;
    jmethodID main_method;
    jmethodID evaluate_method;

    objFuncJ_class = (*env)->FindClass(env, "objFuncJ"); 
    evaluate_method = (*env)->GetStaticMethodID(env, objFuncJ_class, "evaluate", "([D)D"); 
    result = (*env)->CallStaticDoubleMethod(env, objFuncJ_class, evaluate_method, newArray);

    printf("Result = %f\n",result);
    return result;
}

我在我的 Fortran 程序 (mod.f95) 中为函数 init 添加了一个接口,并在调用 obj(double *, int *) 之前调用了 init()。它工作正常,我得到以下输出:

Launched JVM successfully
Result = -1.011206
Result = -1.011206
Result = -1.011206
Result = -1.011206
  -1.0112061116510644  

感谢您的帮助:)