有没有更优雅的方法来基于列表启动线程?

Is there a more elegant way to launch a thread based on a list?

我的 program/class 正在获取需要在并行线程中 运行 的 classes(例如 C-1() 到 C-100())的列表。每个都有自己的 Class 并且有自己的可执行文件,所以我不需要编译,只需 运行。虽然每个 class 都有一个参数,但每个内部的逻辑可能非常不同。所以没有希望多次启动一个带有参数的 class 。

classes 的列表是可变的。可能有一个 class (C-3()) 或多个 (C-1(),C-2(),C-4(),C-3()),它们可能是也可能不是以任何顺序。

我使用了带有循环和 switch 语句的批量方法,但是对其中的 100 条代码进行编码似乎不必要地复杂,而且坦率地说看起来很糟糕。但它有效,最坏的情况下,也能完成工作。但这让我很困扰。

case ("C-1")
{
   new C-1("parm").start();
}
etc .... x 100

lambda 函数可能会让我到达那里,但它超出了我的经验。

我不想 shell 出来。这似乎既低效又可能成为性能杀手。

在理想情况下,我会动态地从列表中拉出该项目并启动它。但我不知道如何动态替换对象名。我不想通过任何巧妙的链接来减慢它的速度。我的专业知识不足以解决这个问题。

最好添加一些东西,这样如果列表少于 10 个,它就会 运行 在同一个线程中,只有在超过 10 个时才会大规模并行。但这也不在我的专业范围内。

公认的解决此问题的最佳方法是使用 ThreadPool。这个想法是您将生成已知数量的线程,并使用这些工作线程来处理任务队列。线程本身可以被重用,避免创建线程的开销。

https://howtodoinjava.com/java/multi-threading/java-thread-pool-executor-example/

package com.howtodoinjava.threads;

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class ThreadPoolExample
{
    public static void main(String[] args)
    {
        ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);

        for (int i = 1; i <= 5; i++)
        {
            Task task = new Task("Task " + i);
            System.out.println("Created : " + task.getName());

            executor.execute(task);
        }
        executor.shutdown();
    }
}

In a perfect world, I would dynamically pull the item from the list and launch it. But I cant figure out how to replace the objectname dynamically.

用于这种动态操作的 Java 子系统和技术称为 "reflection"。 java.lang.Class class 在这里起着核心作用,其余大部分关键 classes 来自包 java.lang.reflect。反射允许您为您通过名称识别的 class 获取 Class 对象,创建该 class 的实例,并在这些实例上调用方法。

如果你的 C-* classes 都有一个通用的 superclass 或定义 start() 方法的接口 (Thread?) 那么你甚至可以执行普通方法调用而不是反射。

前提是你要动态实例化的所有classes都提供接受相同参数类型的构造函数,并且你要向其传递相同的参数值,就可以用它来省写100路条件语句,或一百个不同的适配器 classes,或类似的,适合你的情况。从原理上讲,它将按照以下方式工作:

  • 为想要的 class 获取或创建一个完全限定的 class 名称,比方说 className.

  • 获取对应的Class

    Class<?> theClass = Class.forName(className);
    
  • 获得一个 Constructor 表示您要使用的构造函数。在您的示例中,构造函数采用与 String 兼容的类型的单个参数。如果声明的参数类型实际上是 String 本身(与 ObjectSerializable 或 ... 相对),那么可以这样做:

    Constructor<?> constructor = theClass.getConstructor(String.class);
    
  • 有了它,您可以实例化 class:

    Object theInstance = constructor.newInstance("parm");
    

你从那里开始的路径取决于是否有一个共同的超类型,如上所述。有的话可以

  • 投射实例并正常调用其上的方法:

    ((MySupertype) theInstance).start();
    

否则,您也需要以反射方式调用该方法。由于感兴趣的方法不采用任何参数,这在一定程度上简化了这一点:

  • 获得一个Method实例。

    Method startMethod = theClass.getMethod("start");
    
  • 调用对象上的方法

    startMethod.invoke(theInstance);
    

你也提到,

It would also have been nice to add something so that if the list is less than 10, it would run it in the same thread and only go massively parallel if it was above that.

上面的

None 与开始 运行 您的代码的新线程有任何直接关系。如果那是 start() 方法会自己做的事情(例如,如果涉及的 classes 有 java.lang.Thread 作为超级 class)那么避免每个对象的唯一选择运行在自己的线程上使用不同的方法。

另一方面,如果您从 运行 在一个线程中开始所有事情并希望并行化,那么使用 中描述的线程池是一个很好的方法.请注意,如果任务彼此独立,就像您描述的那样,那么尝试确保它们全部 运行 并发就没有多大意义。线程数量超过 运行 内核并不能真正帮助您,线程池对于排队任务以进行并行执行很有用。当然,检查列表的 size() 来决定是将任务发送到线程池还是直接 运行 它们会很简单。