【问题标题】:Is there a more elegant way to launch a thread based on a list?有没有更优雅的方式来基于列表启动线程?
【发布时间】:2019-08-14 15:44:38
【问题描述】:

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

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

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

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

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

我不想把它掏空。这似乎既低效又可能是性能杀手。

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

添加一些东西也很好,如果列表少于 10,它会在同一个线程中运行它,并且只有在它高于该线程时才会大规模并行。但这也超出了我的专业范围。

【问题讨论】:

  • 每个类的构造函数参数的类型是否相同?这些类有共同的超类型吗?
  • 我们需要更多信息——例如,每个类名是否与您对switch 语句的输入完全相同? ——但我认为约翰有权这样做。可能你必须通过反射来做到这一点。

标签: java multithreading dynamic scheduling


【解决方案1】:

在一个完美的世界中,我会动态地从列表中拉出项目 并启动它。但我不知道如何替换对象名 动态的。

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

如果您的 C-* 类都具有定义 start() 方法 (Thread?) 的通用超类或接口,那么您甚至可以执行普通方法调用而不是反射。

如果你想要动态实例化的所有类都提供了接受相同参数类型并且你想要传递相同参数值的构造函数,你可以使用它来节省编写 100 路条件,或者一百个不同的适配器类或类似的,适合您的情况。从示意图上看,它会按照以下方式工作:

  • 为想要的类获取或创建一个完全限定的类名,比如className

  • 获取对应的Class

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

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

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

您从那里的路径取决于是否存在共同的超类型,如上所述。如果有,那么你可以

  • 强制转换实例并正常调用其上的方法:

    ((MySupertype) theInstance).start();
    

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

  • 获取Method 实例。

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

    startMethod.invoke(theInstance);
    

你也提到了,

如果列表是 小于 10,它将在同一个线程中运行,并且只会大量运行 如果高于此值,则平行。

以上都与启动运行代码的新线程没有任何直接关系。如果这是 start() 方法自己会做的事情(例如,如果所涉及的类将 java.lang.Thread 作为超类),那么避免每个对象在自己的线程上运行的唯一替代方法是使用不同的方法。

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

【讨论】:

    【解决方案2】:

    公认的解决此问题的最佳方法是使用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();
        }
    }
    

    【讨论】:

    • 我不认为这解决了 OP 的主要问题,我认为这是“我无法弄清楚如何动态替换对象名”。
    • 啊。我被Java标签愚弄了。大概这应该是一个*sh 标签? (“每个都是自己的类,有自己的可执行文件,所以我不需要编译,只需运行。”)
    • 不,我认为这是一个 Java 问题。特别是,我认为 OP 正在寻找反思,我已经开始沿着这些思路寻找答案。
    • 啊,是的,它们都有超类型。是的,它们都具有相同的构造函数语法。有些人暂时忽略额外的参数(我假设未来的功能即将推出)
    • 谢谢 - 两个精彩的答案。我得再研究一下反射,这不是我见过的。
    猜你喜欢
    • 2011-06-07
    • 1970-01-01
    • 2016-01-23
    • 2022-11-13
    • 1970-01-01
    • 2019-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多