简体   繁体   English

Java多线程,从单独的类访问列表

[英]Java multithreading, accessing list from separate classes

Just to start off, I'm pretty inept at Java and especially multithreading, so what I'm asking might sound a bit ordinary. 刚开始时,我对Java尤其是多线程技术无能为力,所以我要问的听起来有些普通。 I am attempting to create a program in which I create three threads which each accomplish the specific task of depicting certain values between ten integers, such as average, deviation, etc. How would I approach this? 我正在尝试创建一个程序,在其中创建三个线程,每个线程完成描绘十个整数之间的某些值(例如平均值,偏差等)的特定任务。我将如何处理呢?

I am attempting to create four classes, one for the main program, and three for each of the calculations of the values between each: class "Average" for the average of the ten numbers in the array, class "Median" for the median, etc. The code for the other 3 classes I can easily write, no problem there. 我正在尝试创建四个类,一个用于主程序,三个用于每个之间的值计算:类“ Average”表示数组中十个数字的平均值,类“ Median”表示中位数,等。我可以轻松编写其他3个类的代码,那里没有问题。 My main problem is that since the list "integers" is not available outside the class, I can't write the code for finding each of the values I need in the three programs. 我的主要问题是,由于“ integers”列表在类之外不可用,因此我无法编写代码来查找三个程序中所需的每个值。

Is there a better way to write this so I can actually access the list from inside the classes for each of the threads? 有没有更好的方法来编写此代码,以便实际上可以从每个线程的类内部访问列表?

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
    }
}

There are a lot of options to achieve what you are trying to do. 有很多选择可以实现您想要做的事情。 I will outline two: 我将概述两个:

  • each computation method implementing the Callable interface and taking data into the instance constructor; 实现Callable接口并将数据带入实例构造函数的每种计算方法;
  • each computation method implementing the Function interface and passing data into the call via the closure. 每个计算方法都实现了Function接口,并通过闭包将数据传递到调用中。

It is generally advisable to program to interfaces, that is require an interface as method argument . 通常建议对接口进行编程,即需要接口作为方法参数 All below examples follow this by implementing Callable or Function and working with those high level interfaces elsewhere. 下面的所有示例均通过实现CallableFunction并与其他位置的那些高级接口一起使用。 The code for both cases looks very similar with the main difference being the remapping of Function to Callable in the latter case using the closure state. 两种情况下的代码看起来都非常相似,主要区别是在后一种情况下使用闭包状态将Function重映射为Callable

Let's start with some common utilities (statics for brevity only): 让我们从一些常见的实用程序开始(仅出于简洁起见,使用静态函数):

The following method will create a Collection of 100 random integers in [0,100]: 以下方法将在[0,100]中创建100个随机整数的Collection

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

The following method will execute a collection of Callable s concurrently on a cached executor pool. 以下方法将在缓存的执行程序池上同时执行Callable的集合。 Each callable is generic and will deliver a double value. 每个可调用对象都是通用的,并且将提供双精度值。 Those values (in random order) will be collected and returned as a list: 这些值(以随机顺序)将被收集并作为列表返回:

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;
}

Now let's get back to the core logic. 现在让我们回到核心逻辑。

Case 1: Implementing Callable 情况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());
    }
}

Note: whenever you take a collection as a constructor argument, do not store the reference to the original collection provided in a private field, copy values. 注意:每当将集合用作构造函数参数时,请勿将对私有集合中提供的原始集合的引用存储在私有字段中,请复制值。 If collections are very large, do not pass them to constructor or make unmodifiable. 如果集合很大,请勿将其传递给构造函数或使其不可修改。

@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);
}

Case 2: Implementing Function 情况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);
}

I personally prefer the latter one because your computation methods become generic functions, that is implementing the generic Function interface and can be used in other contexts. 我个人更喜欢后者,因为您的计算方法成为通用函数,即实现通用Function接口并且可以在其他上下文中使用。

Reworking case 1 and 2 with lambdas 使用Lambda重新处理案例1和案例2

You can do some interesting things with lambdas here. 您可以在这里使用lambda做一些有趣的事情。 For the case of functions, you can just predefine them as lambdas instead of constructing new instances of specifically defined class: 对于函数,您可以将它们预定义为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());
    };

Followed later by: 随后是:

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

For the case of callable you can nicely inline them: 对于可调用的情况,您可以很好地内联它们:

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());
});

Note how you do not need external declarations in the latter case. 请注意,在后一种情况下,您不需要外部声明。

Note : as you do not want your results in a random order you will need you function return a pair, eg Map.Entry , with a key and a value. 注意 :由于您不希望结果以随机顺序出现,因此您需要函数返回一对,例如Map.Entry ,并带有一个键和一个值。 But I will leave that to you to exercise. 但我会留给您锻炼。

A sample execution for one of those methods would print something like 这些方法之一的示例执行将显示类似

[53.01,57.0] [53.01,57.0]

You can pass as a constructor argument. 您可以将其作为构造函数参数传递。 Also, after starting the threads, you must call join , otherwise the main thread will not wait the others to finish: 同样,在启动线程之后,必须调用join ,否则主线程将不会等待其他线程完成:

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

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

``` ```

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM