刚开始,我对Java,特别是多线程非常不熟悉,所以我所要求的可能听起来有点普通。我正在尝试创建一个程序,在其中我创建三个线程,每个线程完成描述十个整数之间的某些值的特定任务,例如平均值,偏差等。我将如何处理此问题?
我正在尝试创建四个类,一个用于主程序,三个用于每个类之间的值的每个计算:class" Average"对于数组中十个数字的平均值,类"中位数"对于中位数等,我可以轻松编写其他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
}
}
答案 0 :(得分:0)
有很多选择可以实现您的目标。我将概述两个:
Callable
接口并将数据带入实例构造函数的每个计算方法; Function
接口并通过闭包将数据传递到调用中的每种计算方法。通常建议对接口进行编程,即需要接口作为方法参数。以下所有示例都是通过实现Callable
或Function
并在其他地方使用这些高级接口来实现的。两种情况的代码看起来非常相似,主要区别在于使用闭包状态在后一种情况下将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());
}
以下方法将在缓存的执行程序池上并发执行Callable
的集合。每个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
接口,可以在其他上下文中使用。
使用lambdas重新处理案例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 :(得分:-1)
您可以作为构造函数参数传递。此外,在启动线程后,您必须调用join
,否则主线程将不会等待其他线程完成:
average.start();
median.start();
deviation.start();
average.join();
median.join();
deviation.join();
```