为什么我不能同时执行来自 Java 的不同 Matlab 函数?

Why can't I execute different Matlab functions from Java concurrently?

我有两个 Java Servlet:DataFetcherServletUploaderServlet。两个 servlet 调用 2 个不同的 Java 方法,这些方法又通过 JNI 调用它们相应的 Matlab 函数,每个方法都被编译成一个单独的 Java jar 文件以用作库。该应用程序由 AJAX 提供支持,可创建类似桌面的感觉。对于 UploaderServlet,用户可以上传一个 excel 文件到这个 servlet,然后将解析后的数据传递给 Java 方法,然后调用编译后的 Matlab 函数生成并保存大量图片(目前超过 5000 张图片),因为这会花费很多时间,所以我使用 ExecutorService 在后台执行它。但是新的请求发送 DataFetcherServlet 这也会调用另一个编译的 Matlab 函数被阻塞,直到图像生成部分完成。我不知道为什么它会阻止新请求,即使请求被发送到不同的 servlet。

DataFetcherServlet.java

public class DataFetcherServlet extends HttpServlet {

    @Inject
    private CdfReader reader; // An EJB to get a data array from Matlab

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        try {
            String filePath = "path/to/file";
            Object[] result = reader.read(filePath); // reader.read() is just a wrapper around the method in the jar file mentioned above that actually calls the matlab function to return an array of number
            MWNumericArray array = (MWNumericArray)result[0] // This will block while the other Matlab function is generating the images.
            .
            .
            .
        } catch (MWException ex) {
            Logger.getLogger(DataFetcherServlet.class.getName()).log(Level.SEVERE, null, ex);
    }
}

UploaderServlet.java

public class UploaderServlet extends HttpServlet {
    @Inject
    private ExcelIonImageGenerator generator; // An EJB to call Matlab to generate the images

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        try {
            String dir = "path/to/parent/directory";
            Path excel = Paths.get(dir+ "excel", part.getSubmittedFileName()); // Path to where the uploaded excel file is stored
            if (!Files.exists(excel))
                Files.copy(part.getInputStream(), excel);
            // ExcelExtractor is a helper class to parse the excel file.
            Double[][] ranges = ExcelExtractor.extractSheet(WorkbookFactory.create(excel.toFile()));
            // This will call a Java library method which in turns call the Matlab function
            // to generate the images (over 5000 in this case)
            // See the code for this method below.
            generator.generate(dir+ "images" + File.separator, ranges);
        } catch (MWException | InvalidFormatException ex) {
            Logger.getLogger(UploaderServlet.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

ExcelIonImageGenerator.java

import com.mathworks.toolbox.javabuilder.*; // Matlab SDK needed to integrate with Java
import java.util.concurrent.*;
import java.util.logging.*;
import javax.annotation.PreDestroy;
import javax.ejb.Stateless;
import save_ion_image_for_all_ranges_in_spreadsheet.Class1; // The jar file which contains code to call Matlab code through JNI

@Stateless
public class ExcelIonImageGenerator {
    private final Class1 clazz1;
    private ExecutorService pool;

    public ExcelIonImageGenerator() throws MWException {
        clazz1 = new Class1();
        pool = Executors.newFixedThreadPool(1);
    }

    public void generate(String path, Double[][] ranges) throws MWException {
        // Submit this task to the ExecutorService so it can be processed
        // in a different thread than the caller thread
        pool.submit(() -> generateHelper(path, ranges, clazz1), 1);
    }

    private void generateHelper(String path, Double[][] ranges, Class1 clazz) {
        try {
            // This method was generated by Matlab tool, it calls the native
            // Matlab code through JNI, and it will block any request that will call
            // other Matlab functions until it finishes.
            clazz.save_ion_image_for_all_ranges_in_spreadsheet(path, ranges);
        } catch (MWException ex) {
            Logger.getLogger(ExcelIonImageGenerator.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

您有三个选择:

  1. 启动调用 Matlab 的 Java 应用程序的多个进程。来自单个进程的调用使用具有进程范围锁的相同 MCR,但是来自不同进程的调用将 运行 在不同的 MCR 计算引擎上。
  2. 使用Matlab Production Server,基本上方便了多个MCR的使用。这是一个需要单独许可和安装的工具包。
  3. 除非您有非常具体的性能问题,否则您不必将自己局限于 运行ning MCR/编译代码。您实际上可以在服务器上安装 Matlab 本身,从同一个 Java 进程启动多个实例(无头等),并与它们通信,例如通过 MatlabControl or the new official MATLAB Engine API for Java.

MatlabCentral上有个很好的answer from MathWorks Support Team,详细解释了MCR的这些局限性