在Java中使用可变数量的线程



我正在尝试用java编写一些代码,使用可变数量的"p"线程来计算数组中元素的平均值。

我创建了这个类来表示每个线程:

class Worker extends Thread {
int[] a;
int low;
int upp;
double avg = 0;
public Worker(int[] a, int low, int upp){
this.a = a;
this.low = low;
this.upp = upp;
}
public void run(){
int sum = 0;
for (int i = low; i < upp; i++){
sum+=a[i];
}
avg = (double)sum/a.length;
}
}

这是计算平均值的代码:

static double parallelaverage(int a[], int p) {
int avg = 0;
int num = a.length/p;
ArrayList<Worker> threads = new ArrayList<Worker>();
for (int i = 0; i<a.length; i+=num){
if (i+num > a.length){
Worker x = new Worker(a, i, a.length);
x.start();
threads.add(x);
}
else{
Worker x = new Worker(a, i, i+num);
x.start();
threads.add(x);

}
}
try{for (Worker thread: threads){
thread.join();}
} catch (Exception e){}
for (Worker thread: threads){
avg += thread.avg;
}
return avg;
}

这对我来说似乎有道理,但我得到的实际结果总是与实际平均值相去甚远。我的代码出了什么问题?

您的问题是类型在语言中执行算术的实际方式。方程中使用的所有数字都必须是相同的类型,然而,这对于除法至关重要。结果可能需要是浮点或双!

// avg = (double)sum/a.length;
// Should be done as
double avg = (new Integer(sum).doubleValue()/new Integer(a.length).doubleValue());

您经常会收到编译器关于";精度损失";当在一个等式中使用多种类型时,语言会允许。

关于我们如何提高现有代码质量的两个想法

个人注意:你的代码中有一些错误,我知道你试图实现什么,并且做得更好。

1.依赖ExecutiorService

为什么不利用执行器服务呢?您不必手动控制线程。

class Scratch {
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println(parallelAverage(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11}, 2));
}
static double parallelAverage(int a[], final int perSlice) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(a.length / perSlice);
List<Future<Double>> futureList = new ArrayList<>();
int start, end;
start = end = 0;
for (; ; ) {
if (a.length <= end) {
break;
}
start = end;
end = Math.min((int) (end + perSlice), a.length);
futureList.add(executorService.submit(new Worker(a, start, end)));
}

double avg = 0;
for (Future<Double> doubleFuture : futureList) {
Double aDouble = doubleFuture.get();
avg += aDouble;
}
executorService.shutdown();
return avg;
}
}

2.使用可调用而非可运行来返回值

Worker类中,与其使用Runnable接口,不如使用Callable接口。这允许您返回一个值(Future(。

final class Worker implements Callable<Double> {
int[] a;
int low;
int upp;
public Worker(int[] array, final int startInclusive, final int endExclusive) {
this.a = array;
this.low = startInclusive;
this.upp = endExclusive;
}
@Override
public Double call() throws Exception {
int sum = 0;
for (int i = low; i < upp; i++) {
sum += a[i];
}
return (double) sum / a.length;
}

问题是如何初始化avg变量:

int avg = 0;

它是一个int,每次添加Worker.avg(即double(时,小数部分都会丢失,0 + 0.3 = 01 + 2.7 = 3等等。使用调试器可以很容易地观察其行为。

您需要将其初始化为双重:

double avg = 0;

另外注意,这是代码重复,有些不清楚:

if (i+num > a.length){
Worker x = new Worker(a, i, a.length);
x.start();
threads.add(x);
}
else{
Worker x = new Worker(a, i, i+num);
x.start();
threads.add(x);
}

更好的编写方法是使用Math.min((:

int upp = Math.min(i + num, a.length);
Worker x = new Worker(a, i, upp);
x.start();
threads.add(x);

最新更新