Java 使用 ArrayList 时 ExecutorService 比 serial 慢吗?

Java ExecutorService slower than serial in when using ArrayList is it ?

我正在尝试将一个大列表拆分为两个任务并执行一些处理,然后将它们合并。在下面的测试中,我发现串行处理比并行处理快很多(或者)我做错了什么。 下面是结果,

sh-4.3$ java -Xmx128M -Xms16M FutureTaskDemo

I =2  
Sequential Result         : 2000000 calculated in 2339 ms 
First Value:2 List Value:2000001                          
Initial List First Value:2 List Value:2000001             
I =1000004                                                
I =4  
1000000
1000000                                                   
Merging                                                   
Parallel Result         : 2000000 calculated in 3207 ms   
First Value:4 List Value:2000003                               

下面是代码,

import java.util.ArrayList;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

public class FutureTaskDemo {

/**
 * Maximum amount of numbers to check
 */
public static final int MAX_NUMBER = 2_000_000;

public static List<Integer> addOneToListS(List<Integer> list) {
Integer flag=0;
for(Integer i = 0;i<list.size();i++)
{
    if(flag==0) System.out.println("I ="+(list.get(i)+2));
   list.set(i, list.get(i) +2);
   flag=1;
}

    return list;
}

public static List<Integer> addOneToListP(List<Integer> list)
        throws InterruptedException, ExecutionException {

    List<Integer> listA = new ArrayList<>(list.subList(0,MAX_NUMBER/2));
    List<Integer> listB = new ArrayList<>(list.subList(MAX_NUMBER/2,MAX_NUMBER));

    // Prepare to execute and store the Futures
    int threadNum = 2;
    ExecutorService executor = Executors.newFixedThreadPool(threadNum);
    List<FutureTask<List<Integer>>> taskList = new ArrayList<FutureTask<List<Integer>>>();

    // Start thread for the first half of the numbers
    FutureTask<List<Integer>> futureTask_1 = new FutureTask<List<Integer>>(new Callable<List<Integer>>() {
        @Override
        public List<Integer> call() {
            return FutureTaskDemo.addOneToListS(listA);
        }
    });
    taskList.add(futureTask_1);
    executor.execute(futureTask_1);

    // Start thread for the second half of the numbers
    FutureTask<List<Integer>> futureTask_2 = new FutureTask<List<Integer>>(new Callable<List<Integer>>() {
        @Override
        public List<Integer> call() {
            return FutureTaskDemo.addOneToListS(listB);
        }
    });
    taskList.add(futureTask_2);
    executor.execute(futureTask_2);

    // Wait until all results are available and combine them at the same time
Integer totalsize = 0;
    for (int j = 0; j < threadNum; j++) {
        FutureTask<List<Integer>> futureTask = taskList.get(j);
        System.out.println(futureTask.get().size());
    }
    executor.shutdown();
    System.out.println("Merging");
    List<Integer> fullList = new ArrayList<>();
    fullList.addAll(futureTask_1.get());
    fullList.addAll(futureTask_2.get());

    return fullList;
}

public static void main(String[] args) throws InterruptedException, ExecutionException {

    // Sequential execution
List<Integer> initialList = new ArrayList<>();
for(int i=0;i<MAX_NUMBER;i++)
    initialList.add(i);

    long timeStart = Calendar.getInstance().getTimeInMillis();
    List<Integer> addedList1 = FutureTaskDemo.addOneToListS(initialList);
    long timeEnd = Calendar.getInstance().getTimeInMillis();
    long timeNeeded = timeEnd - timeStart;
    System.out.println("Sequential Result         : " + addedList1.size()+ " calculated in " + timeNeeded + " ms");
    System.out.println("First Value:"+addedList1.get(0)+" List Value:"+addedList1.get(addedList1.size()-1));

    System.out.println("Initial List First Value:"+initialList.get(0)+" List Value:"+initialList.get(initialList.size()-1));
     timeStart = Calendar.getInstance().getTimeInMillis();
    List<Integer> addedList2 = FutureTaskDemo.addOneToListP(initialList);
     timeEnd = Calendar.getInstance().getTimeInMillis();
     timeNeeded = timeEnd - timeStart;
    System.out.println("Parallel Result         : " + addedList2.size()+ " calculated in " + timeNeeded + " ms");
    System.out.println("First Value:"+addedList2.get(0)+" List Value:"+addedList2.get(addedList2.size()-1));


}

}

我会使用并行流

public static List<Integer> addOneToListP(List<Integer> list) {
    return list.parallelStream().map(i -> i + 1).collect(Collectors.toList());
}

这更短、更简单、更容易理解。它是否更快,将取决于你正在做的事情是通过拥有更多的 CPU 来加速,还是使用多线程和在它们之间传递数据的开销太高。您的并行版本正在对数据进行大量复制。

您最大的开销可能是您创建的垃圾率非常高。我会看看没有这么多垃圾的集合会发生什么。

public static int[] addOneToListP(int[] array) {
    return IntStream.of(array).parallel().map(i -> i + 1).toArray();
}

无论有没有多个 CPU,这应该使用一小部分内存并且速度要快得多。

注意:您的 addOneToListS 似乎添加了 2,这令人困惑。