ThreadPool工作不可预测

时间:2014-09-27 20:25:19

标签: java concurrency threadpool

我的任务很简单: 有数组列表,我需要使用线程池找到所有数字除以10的数字。

这是我的代码

import java.util.List;

Runnable实施:

public class MyRunnable implements Runnable {
    private List<Integer> numbers;
    private int[] arrayToFind;

    public MyRunnable(List<Integer> numbers, int[] arrayToFind) {
        this.numbers = numbers;
        this.arrayToFind = arrayToFind;
    }

    private boolean isNumber(int n) {
        return n % 10 == 0;
    }

    @Override
    public void run() {
        for (int i = 0; i < arrayToFind.length; i++) {
            if (isNumber(arrayToFind[i]))
                numbers.add(arrayToFind[i]);
        }
    }
}

主要课程:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {

    public static void main(String... args) throws InterruptedException{
        int[] arr1 = new int[] {10};
        int[] arr2 = new int[] {400};
        int[] arr3 = new int[] {20};
        int[] arr4 = new int[] {40};
        List<int[]> list = new ArrayList<int[]>();
        list.add(arr1);
        list.add(arr2);
        list.add(arr3);
        list.add(arr4);

        List<Integer> result = new ArrayList<Integer>();

        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int[] array : list) {
            executorService.execute(new MyRunnable(result, array));
        }
        executorService.shutdown();

        System.out.println(result);
    }
}

问题是输出有时是正确的,因为它应该是{10,20,40,400}但有时它是{},有时它是{40,20}等。

你怎么解释这个?

4 个答案:

答案 0 :(得分:2)

默认情况下插入列表are not synchronized,因此不是线程安全的。如果你想实现线程安全(这样你就不会丢失数据),那么将你的实例包装在Collections.synchronizedList内。

List<Integer> result = Collections.synchronizedList(new ArrayList<Integer>());

现在你的结果将全部出现(当然仍然是非确定性的顺序)。

答案 1 :(得分:1)

在ExecutorService.shutdown的API中,它说:“此方法不会等待先前提交的任务完成执行。请使用awaitTermination来执行此操作。”

尝试使用awaitTermination。我打赌这会解决你的问题

答案 2 :(得分:0)

似乎有时ExecutorService会关闭,直到某些或你的MyRunnable.run完成。一般来说,ExecutorService.execute + Runnable允许你做一些工作&#34;当你对结果不感兴趣的时候。如果您需要等到计算所有结果,请使用ExecutorService.invokeAll + Callable。例如见:

http://tutorials.jenkov.com/java-util-concurrent/executorservice.html

最后,将计算结果存储在线程安全集合中:

public static class MyCallable implements Callable<Boolean> {
    private Set<Integer> numbers;
    private int[] arrayToFind;

    public MyCallable(Set<Integer> numbers, int[] arrayToFind) {
        this.numbers = numbers;
        this.arrayToFind = arrayToFind;
    }

    private boolean isNumber(int n) {
        return n % 10 == 0;
    }

    @Override
    public Boolean call() {
        for (int i = 0; i < arrayToFind.length; i++) {
            if (isNumber(arrayToFind[i]))
                numbers.add(arrayToFind[i]);
        }
        return true;
    }
}


public static void main(String... args) throws InterruptedException{
    int[] arr1 = new int[] {10};
    int[] arr2 = new int[] {400};
    int[] arr3 = new int[] {20};
    int[] arr4 = new int[] {40};
    List<int[]> list = new ArrayList<>();
    list.add(arr1);
    list.add(arr2);
    list.add(arr3);
    list.add(arr4);

    Set<Integer> result = Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
    List<MyCallable> myCallables = new ArrayList<>();
    for (int[] array : list) {
        myCallables.add(new MyCallable(result, array));
    }
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    executorService.invokeAll(myCallables);
    executorService.shutdown();
    System.out.println(result);
}

答案 3 :(得分:0)

在打印结果之前,您不是在等待任务完成。这就是为什么不一致。而不是执行,使用submit方法向执行程序提交任务,因为它返回一个Future实例,可用于等待任务完成。

结帐Future's javadoc

public static void main(String[] args) throws InterruptedException, ExecutionException {
    int[] arr1 = new int[] { 10 };
    int[] arr2 = new int[] { 400 };
    int[] arr3 = new int[] { 20 };
    int[] arr4 = new int[] { 40 };
    List<int[]> list = new ArrayList<int[]>();
    list.add(arr1);
    list.add(arr2);
    list.add(arr3);
    list.add(arr4);

    List<Integer> result = new ArrayList<Integer>();
    List<Future<?>> futures = new ArrayList<Future<?>>();
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    for (int[] array : list) {
        futures.add(executorService.submit(new MyRunnable(result, array)));
    }
    executorService.shutdown();

    for (Future<?> future : futures) {
        future.get();
    }

    System.out.println(result);
}