【问题标题】:Moving threads into another class Java将线程移动到另一个 Java 类中
【发布时间】:2021-09-22 12:43:09
【问题描述】:

我有一个简单的 Java 线程分配,它使用线程计算平均值、最大值和最小值。目前,我的所有线程都在一个类中。如何将这些线程移动到单独的“计算类”中,然后从 main 中的外部类调用线程?以下是我的代码,不胜感激!

import java.util.*;

public class Stats {
// Declaring the necessary variables.
static int x;
       static int[] input;
// Array of integers to hold the maximum and minimum values when computed in the
// thread. placeHolder[0] = min placeHolder[1] = max.
static int[] placeHolder = new int[2];
static double[] averageHolder = new double[1];

public static void main(String[] args) {
    // Print statements prompting user/gathering input and storing into the array.
    Scanner scan = new Scanner(System.in);
    System.out.println("How many numbers would you like to compute?");
    x = scan.nextInt();
    input = new int[x];
    System.out.println("Please enter the numbers you would like computed:");
    for (int i = 0; i < x; i++) {
        input[i] = scan.nextInt();
    }
    avgThread.start();
    minThread.start();
    maxThread.start();

    try {
        avgThread.join();
        minThread.join();
        maxThread.join();
    } catch (Exception i) {

    }

    System.out.printf("Avarage of Data : %f\nMinimum of Data : %d\nMaximum of Data :%d\n", averageHolder[0],
            placeHolder[0], placeHolder[1]);
}

static Thread maxThread = new Thread(new Runnable() {
    @Override
    public void run() {
        int maximum = input[0];
        int i = 0;
        while (i < x) {
            if (maximum < input[i])
                maximum = input[i];
            i++;
        }
        placeHolder[1] = maximum;
    }
});

static Thread avgThread = new Thread(new Runnable() {
    @Override
    public void run() {
        double average = 0;
        int i = 0;
        while (i < x) {
            average = average + input[i];
            i++;
        }
        average = average / x;
        averageHolder[0] = average;
    }
});

static Thread minThread = new Thread(new Runnable() {
    @Override
    public void run() {
        int minimum = input[0];
        int i = 0;
        while (i < x) {
            if (minimum > input[i])
                minimum = input[i];
            i++;
        }

        placeHolder[0] = minimum;
    }
});

}

【问题讨论】:

  • 创建线程并保持对它们的引用与类和实例中的任何其他数据没有什么不同。您可能想要考虑制作实现Runnable 的类,并将其结果保存在内部并将输入数组作为参数。尝试先建立一个合理的结构。
  • 所以你说的是创建线程。然后在 main 中声明线程,如 Thread maxThread = new Therad();?
  • Stats中的相关代码提取到另一个类如StatsCalculator。在main() 中创建StatsCalculator 的新实例并调用运行线程的StatsCalculator 方法。另外,考虑返回类似StatsResult 类实例来保存结果,而不是返回一个神奇的位置数组。
  • 谢谢安德鲁。最后一个问题。由于我现在正在StatsCalculator 中进行计算,那么我会在该类中声明变量吗?因为扫描仪如何链接到新类?

标签: java multithreading class


【解决方案1】:

tl;博士

你问:

如何将这些线程移到单独的“计算类”中

为每个任务定义一个类,其中每个类都实现Runnable(或Callable)接口。

class Avg implements Callable < Double > { … }

你的标题要求:

将线程移动到另一个 Java 类中

使用executor service 对线程进行分组和管理。

ExecutorService executorService = Executors.newFixedThreadPool( 3 );
Future < Double > futureAvg = executorService.submit( new Avg( … ) )

详情

避免使用static,因为它不是面向对象的。

在现代 Java 中,我们很少直接寻址 Thread。相反,我们使用 Executors 框架。见tutorial by OracleExecutorService 抽象出处理线程。

当您希望后台任务返回值时,请使用 Callable 而不是 Runnable。当我们将每个Callable(平均值、最小值、最大值)提交给我们的执行器服务时,我们会返回一个Future,它最终会保存我们的结果。

在提交我们所有的Callable 任务后,我们要求执行器服务在任务完成(或取消,或失败)后关闭。我们等待一段时间以完成关闭。然后我们询问每个Future 对象的结果。

我们将结果(平均值、最小值、最大值)收集到我们自己设计的自定义类的对象中,以保存这三个值,以透明且不可变的方式传递数据。在 Java 16+ 中,records 特性使编写这样一个类变得非常简单:只需声明每个成员字段的类型和名称。编译器隐式写入构造函数、getter、equals & hashCodetoString

与其拥有一个冗长的main 方法,不如将其分解为目标驱动的块。这里我们分为三个部分:收集输入,处理这些输入以产生结果,最后报告这些结果。

只要我们遵守共享int[] 数组和Results 对象的约定,请注意我们如何在不影响其他部分的情况下更改这三个部分中的任何部分的实现。这个separation of concerns 有助于使您的软件更健壮、更易于维护和发展,并且更易于理解。

Java 有两种类型系统:原语和类(对象)。 Double 是一个包装原始 double 值的类。 Integer/int 同上。 Java 通常可以在称为auto-boxing 的进程中在这些之间自动转换。

Double/double 是等效的64-bit 浮点类型,用于比32-bit Float/float floating-point 类型更大/更小的数字。

package work.basil.threads;

import java.util.Arrays;
import java.util.Objects;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.concurrent.*;

public class Computation
{
    record Results( Double average , Integer min , Integer max ) { }

    private int[] gatherInputs ()
    {
        // …
        return new int[] { 7 , 42 , 99 };
    }

    private Results compute ( int[] inputs )
    {
        ExecutorService executorService = Executors.newFixedThreadPool( 3 );

        Future < Double > futureAvg = executorService.submit( new Avg( Arrays.copyOf( inputs , inputs.length ) ) );
        Future < Integer > futureMin = executorService.submit( new Min( Arrays.copyOf( inputs , inputs.length ) ) );
        Future < Integer > futureMax = executorService.submit( new Max( Arrays.copyOf( inputs , inputs.length ) ) );

        executorService.shutdown();
        try { executorService.awaitTermination( 1 , TimeUnit.MINUTES ); } catch ( InterruptedException e ) { e.printStackTrace(); }
        Results results = null;
        try { results = new Results( futureAvg.get() , futureMin.get() , futureMax.get() ); } catch ( InterruptedException e ) { e.printStackTrace(); } catch ( ExecutionException e ) { e.printStackTrace(); }
        return Objects.requireNonNull( results );
    }

    private void reportResults ( Results results )
    {
        System.out.println( "results = " + results );
    }


    class Avg implements Callable < Double >
    {
        private int[] inputs;

        public Avg ( int[] inputs )  // Constructor
        {
            Objects.requireNonNull( inputs );
            if ( inputs.length == 0 ) { throw new IllegalArgumentException( "The inputs int[] must not be empty." ); }
            this.inputs = inputs;
        }

        @Override
        public Double call () throws Exception
        {
            OptionalDouble optionalDouble = Arrays.stream( inputs ).average();
            return optionalDouble.orElseThrow(); // Should never throw as we verified in constructor that the inputs were not empty.
        }
    }

    class Min implements Callable < Integer >
    {
        private int[] inputs;

        public Min ( int[] inputs )  // Constructor
        {
            Objects.requireNonNull( inputs );
            if ( inputs.length == 0 ) { throw new IllegalArgumentException( "The inputs int[] must not be empty." ); }
            this.inputs = inputs;
        }

        @Override
        public Integer call () throws Exception
        {
            OptionalInt optionalInt = Arrays.stream( inputs ).min();
            return optionalInt.orElseThrow(); // Should never throw as we verified in constructor that the inputs were not empty.
        }
    }

    class Max implements Callable < Integer >
    {
        private int[] inputs;

        public Max ( int[] inputs )  // Constructor
        {
            Objects.requireNonNull( inputs );
            if ( inputs.length == 0 ) { throw new IllegalArgumentException( "The inputs int[] must not be empty." ); }
            this.inputs = inputs;
        }

        @Override
        public Integer call () throws Exception
        {
            OptionalInt optionalInt = Arrays.stream( inputs ).max();
            return optionalInt.orElseThrow(); // Should never throw as we verified in constructor that the inputs were not empty.
        }
    }


    public static void main ( String[] args )
    {
        Computation app = new Computation();
        int[] inputs = app.gatherInputs();
        Results results = app.compute( inputs );
        app.reportResults( results );
    }

}

运行时。

结果 = 结果[平均值=49.333333333333336,最小值=7,最大值=99]

我应该注意,您在此处的特殊情况不适合使用多线程。并发不是魔法,它需要付出代价,"overhead"。计算少量int 值的平均值、最小值和最大值不太可能证明该开销是合理的。但我认为这只是学习并发的练习。为此,这是一个值得练习的练习。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-21
    • 1970-01-01
    • 2017-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多