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

时间:2015-04-02 00:25:41

标签: java c multithreading java-native-interface fortran

我正在尝试将数组从Fortran传递给Java,在Java中进行一些计算并将值返回到我的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一次?

2 个答案:

答案 0 :(得分:0)

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

不同意

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

道德:不要自己尝试编写JNI方法签名。使用javap -s的输出。这绝不是错的。

答案 1 :(得分:0)

好吧我知道我哪里出错了。我按如下方式更改了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  

感谢您的帮助:)