Java多线程,从不同的类访问列表

问题描述 投票:1回答:2

刚开始,我对Java,特别是多线程非常不熟悉,所以我问的可能听起来有点普通。我正在尝试创建一个程序,在其中我创建三个线程,每个线程完成描述十个整数之间的某些值的特定任务,例如平均值,偏差等。我将如何处理此问题?

我试图创建四个类,一个用于主程序,三个用于每个值之间的每个值的计算:类“平均值”表示数组中十个数字的平均值,类别“中位数”表示中位数,我可以轻松编写其他3个类的代码,没有问题。我的主要问题是,由于列表“整数”在课外不可用,我无法编写代码来查找三个程序中我需要的每个值。

有没有更好的方法来编写这个,所以我实际上可以从每个线程的类内部访问列表?

import java.util.*;

public class ThreadDemo
{
    public static void main(String[] args)
    {
        Random number = new Random();
        List integers = new ArrayList();

        for (int i = 0; i < 10; i++)
        {
            integers.add(number.nextInt(101));
        }

        Thread average = new Thread(new Average());
        Thread median = new Thread(new Median());
        Thread deviation = new Thread(new Deviation());

        average.start();
        median.start();
        deviation.start();
    }
}

class Average extends Thread
{
    public void run()
    {
         // code for finding average
    }
}

class Median extends Thread
{
    public void run()
    {
         // code for finding median
    }
}

class Deviation extends Thread
{
    public void run()
    {
         // code for finding deviation
    }
}
java multithreading
2个回答
0
投票

有很多选择可以实现您的目标。我将概述两个:

  • 每个计算方法实现Callable接口并将数据带入实例构造函数;
  • 每个计算方法实现Function接口并通过闭包将数据传递给调用。

通常建议对接口进行编程,即需要将接口作为方法参数。以下所有示例都是通过实现CallableFunction并在其他地方使用这些高级接口来实现的。两种情况的代码看起来非常相似,主要区别在于后一种情况下使用闭合状态将Function重新映射到Callable

让我们从一些常见的实用程序开始(仅用于简洁的静态):

以下方法将在[0,100]中创建100个随机整数的Collection

private static Collection<Integer> ints() {
    Random random = new Random();
    return random.ints(100, 0, 100)
        .boxed()
        .collect(Collectors.toList());
}

以下方法将在缓存的执行程序池上并发执行Callables的集合。每个callable都是通用的,并且会提供double值。这些值(以随机顺序)将被收集并作为列表返回:

private static List<Double> concurrently(Collection<Callable<Double>> callables) throws InterruptedException, ExecutionException {
    ExecutorService executors = Executors.newCachedThreadPool();
    Collection<Future<Double>> futures = executors.invokeAll(callables);
    List<Double> res = new ArrayList<>();
    for (Future<Double> future: futures) {
        res.add(future.get());
    }
    executors.shutdownNow();
    return res;
}

现在让我们回到核心逻辑。

案例1:实施Callable

class Averager<V extends Number> implements Callable<Double> {

    private final Collection<V> values = new ArrayList<>();

    Averager(Collection<V> values) {
        this.values.addAll(values);
    }

    @Override
    public Double call() {
        double sum = 0.0;
        for (V value : values) {
            sum += value.doubleValue();
        }
        return Double.valueOf(sum / values.size());
    }
}

class Medianer<V extends Number> implements Callable<Double> {

    private final Collection<V> values = new ArrayList<>();

    Medianer(Collection<V> values) {
        this.values.addAll(values);
    }

    @Override
    public Double call() {
        List<V> sorted = new ArrayList<>(values);
        sorted.sort(Comparator.comparingDouble(Number::doubleValue));
        // TODO treat odd/even number of elements separately
        return Double.valueOf(sorted.get(sorted.size() / 2).doubleValue());
    }
}

注意:无论何时将集合作为构造函数参数,都不要存储对私有字段中提供的原始集合的引用,请复制值。如果集合非常大,请不要将它们传递给构造函数或使其不可修改。

@Test
public void usingCallable() throws InterruptedException, ExecutionException {
    Collection<Integer> values = ints();

    Collection<Callable<Double>> callables = new ArrayList<>();
    callables.add(new Averager<>(values));
    callables.add(new Medianer<>(values));

    List<Double> res = concurrently(callables);
    System.out.println(res);
}

案例2:实施Function

class Averager<V extends Number> implements Function<Collection<V>, Double> {
    @Override
    public Double apply(Collection<V> values) {
        double sum = 0.0;
        for (V value : values) {
            sum += value.doubleValue();
        }
        return Double.valueOf(sum / values.size());
    }
}

class Medianer<V extends Number> implements Function<Collection<V>, Double> {
    @Override
    public Double apply(Collection<V> values) {
        List<V> sorted = new ArrayList<>(values);
        sorted.sort(Comparator.comparingDouble(Number::doubleValue));
        // TODO treat odd/even number of elements separately
        return Double.valueOf(sorted.get(sorted.size() / 2).doubleValue());
    }
}

@Test
public void usingFunction() throws InterruptedException, ExecutionException {
    Collection<Integer> values = ints();

    Collection<Function<Collection<Integer>, Double>> functions = new ArrayList<>();
    functions.add(new Averager<>());
    functions.add(new Medianer<>());

    List<Callable<Double>> callables = functions.stream().map(f -> (Callable<Double>) () -> f.apply(values)).collect(Collectors.toList());

    List<Double> res = concurrently(callables);
    System.out.println(res);
}

我个人更喜欢后者,因为你的计算方法成为通用函数,即实现通用的Function接口,可以在其他环境中使用。

用lambda重写案例1和2

你可以在这里用lambdas做一些有趣的事情。对于函数的情况,您可以将它们预定义为lambda,而不是构造特定定义的类的新实例:

static final Function<Collection<Integer>, Double> averager = (values) -> {
        double sum = 0.0;
        for (Integer value : values) {
            sum += value.doubleValue();
        }
        return Double.valueOf(sum / values.size());
    };

static final Function<Collection<Integer>, Double> medianer = (values) -> {
        List<Integer> sorted = new ArrayList<>(values);
        sorted.sort(Comparator.comparingDouble(Number::doubleValue));
        // TODO treat odd/even number of elements separately
        return Double.valueOf(sorted.get(sorted.size() / 2).doubleValue());
    };

随后是:

Collection<Function<Collection<Integer>, Double>> functions = new ArrayList<>();
functions.add(averager);
functions.add(medianer);

对于可调用的情况,你可以很好地内联它们:

Collection<Callable<Double>> callables = new ArrayList<>();
callables.add(() -> {
    double sum = 0.0;
    for (Integer value : values) {
        sum += value.doubleValue();
    }
    return Double.valueOf(sum / values.size());
});
callables.add(() -> {
    List<Integer> sorted = new ArrayList<>(values);
    sorted.sort(Comparator.comparingDouble(Number::doubleValue));
    // TODO treat odd/even number of elements separately
    return Double.valueOf(sorted.get(sorted.size() / 2).doubleValue());
});

请注意在后一种情况下如何不需要外部声明。

注意:由于您不希望以随机顺序显示结果,因此您需要使用函数返回一对,例如Map.Entry,有钥匙和价值。但我会留给你锻炼。

其中一个方法的示例执行会打印出类似的内容

[53.01,57.0]


-1
投票

您可以作为构造函数参数传递。此外,在启动线程后,您必须调用join,否则主线程将不会等待其他线程完成:

average.start();
median.start();
deviation.start();

average.join();
median.join();
deviation.join();

```

© www.soinside.com 2019 - 2024. All rights reserved.