Runtime.exec() will launch a separate process for each call. Your Java caller needs to consume the output of each process.
JNI would require a native, dynamically linked library. Depending on your operating system, you may need to explicitly export functions. You would define a Java class with "native" methods, generate a C header/stub file with javah, and implement the native methods by calling your C client functions.
Runtime.exec() probably consumes the most resources. I personally would use an in-process call to native code.
Instead of JNI, consider using JNA, which makes it easy to call C functions from Java without an ad hoc native glue layer. Your C functions would need to be in a native, dynamically linked library. In Java, you declare their signatures, load the library, and call the functions.
For Windows DLLs, be aware that you need to export functions for them to be available from outside the DLL.