Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/326.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
用java并行处理数组_Java_Multithreading - Fatal编程技术网

用java并行处理数组

用java并行处理数组,java,multithreading,Java,Multithreading,我试图通过线程应用程序以获得更快的输出。只是做一个小的POC排序。 假设我有一个问题陈述来查找数组中所有奇数出现的数字。 下面是我对顺序和并行的尝试 import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Random

我试图通过线程应用程序以获得更快的输出。只是做一个小的POC排序。 假设我有一个问题陈述来查找数组中所有奇数出现的数字。 下面是我对顺序和并行的尝试

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

public class Test1 {

    final static Map<Integer, Integer> mymap  = new HashMap<>();

    static Map<Integer, AtomicInteger> mymap1 = new ConcurrentHashMap<>();

    public static void generateData(final int[] arr) {
        final Random aRandom = new Random();
        for (int i = 0; i < arr.length; i++) {
            arr[i] = aRandom.nextInt(10);
        }
    }

    public static void calculateAllOddOccurrence(final int[] arr) {

        for (int i = 0; i < arr.length; i++) {
            if (mymap.containsKey(arr[i])) {
                mymap.put(arr[i], mymap.get(arr[i]) + 1);
            } else {
                mymap.put(arr[i], 1);
            }
        }

        for (final Map.Entry<Integer, Integer> entry : mymap.entrySet()) {
            if (entry.getValue() % 2 != 0) {
                System.out.println(entry.getKey() + "=" + entry.getValue());
            }

        }

    }

    public static void calculateAllOddOccurrenceThread(final int[] arr) {

        final ExecutorService executor = Executors.newFixedThreadPool(10);
        final List<Future<?>> results = new ArrayList<>();
        ;
        final int range = arr.length / 10;
        for (int count = 0; count < 10; ++count) {
            final int startAt = count * range;
            final int endAt = startAt + range;
            executor.submit(() -> {
                for (int i = startAt; i < endAt; i++) {
                    if (mymap1.containsKey(arr[i])) {
                        final AtomicInteger accumulator = mymap1.get(arr[i]);
                        accumulator.incrementAndGet();
                        mymap1.put(arr[i], accumulator);
                    } else {
                        mymap1.put(arr[i], new AtomicInteger(1));
                    }
                }
            });
        }

        awaitTerminationAfterShutdown(executor);

        for (final Entry<Integer, AtomicInteger> entry : mymap1.entrySet()) {
            if (entry.getValue().get() % 2 != 0) {
                System.out.println(entry.getKey() + "=" + entry.getValue());
            }

        }

    }

    public static void calculateAllOddOccurrenceStream(final int[] arr) {

        final ConcurrentMap<Integer, List<Integer>> map2 = Arrays.stream(arr).parallel().boxed().collect(Collectors.groupingByConcurrent(i -> i));
        map2.entrySet().stream().parallel().filter(e -> e.getValue().size() % 2 != 0).forEach(entry -> System.out.println(entry.getKey() + "=" + entry.getValue().size()));

    }

    public static void awaitTerminationAfterShutdown(final ExecutorService threadPool) {
        threadPool.shutdown();
        try {
            if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
                threadPool.shutdownNow();
            }
        } catch (final InterruptedException ex) {
            threadPool.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }

    public static void main(final String... doYourBest) {

        final int[] arr = new int[200000000];

        generateData(arr);
        long starttime = System.currentTimeMillis();
        calculateAllOddOccurrence(arr);

        System.out.println("Total time=" + (System.currentTimeMillis() - starttime));

        starttime = System.currentTimeMillis();
        calculateAllOddOccurrenceStream(arr);

        System.out.println("Total time Thread=" + (System.currentTimeMillis() - starttime));

    }

}
并行执行CalculateAllodoccurrenceStream需要更多时间。并行处理数组然后合并结果的最佳方法是什么


我的目标不是找到最快的算法,而是使用任何算法并尝试在不同的线程中运行,以便它们同时处理阵列的不同部分

您将看到Java 8中引入的STREAMS API:

例如:

// sequential processes
myArray.stream().filter( ... ).map( ... ).collect(Collectors.toList()):

// parallel processes
myArray.parallelStream().filter( ... ).map( ... ).collect(Collectors.toList());

您将看到Java 8中引入的STREAMS API:

例如:

// sequential processes
myArray.stream().filter( ... ).map( ... ).collect(Collectors.toList()):

// parallel processes
myArray.parallelStream().filter( ... ).map( ... ).collect(Collectors.toList());

这些线程似乎同时在阵列的相同部分上工作,因此答案不正确

而是使用适当的开始索引和结束索引将数组分成若干部分。分配单独的线程来处理这些部分,并统计每个部分中每个数字的出现次数

最后,您将有多个贴图,其中的计数是从这些单独的部分计算出来的。合并这些地图以获得最终答案


或者您可以使用一个concurrentHashMap来存储来自所有这些线程的计数,但是我想可能会有一个bug潜入其中,因为仍然会存在并发写入冲突。在高度多线程的环境中,cocnurrentHashMap上的写入可能不是100%安全的。对于有保证的写入行为,正确的方法是使用ConcurrentHashMap.putIfAbsentK键的原子性,V值方法,并注意返回值,该值指示put操作是否成功。简单的put可能不正确。看

您可以使用Java8StreamsAPI编写代码,也可以使用Java5构造编写简单的线程代码

添加了Java8流代码,请注意计时差异。ArrayList而不是数组会产生不同:

package com.test;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Test {

    public static void generateData(final int[] arr) {
        final Random aRandom = new Random();
        for (int i = 0; i < arr.length; i++) {
            arr[i] = aRandom.nextInt(10);
        }
    }

    public static void calculateAllOddOccurrence(final int[] arr) {
        final Map<Integer, Integer> mymap  = new HashMap<>();
        for (int i = 0; i < arr.length; i++) {
            if (mymap.containsKey(arr[i])) {
                mymap.put(arr[i], mymap.get(arr[i]) + 1);
            } else {
                mymap.put(arr[i], 1);
            }
        }
        for (final Map.Entry<Integer, Integer> entry : mymap.entrySet()) {
            if (entry.getValue() % 2 != 0) {
                System.out.println(entry.getKey() + "=" + entry.getValue());
            }

        }
    }

    public static void calculateAllOddOccurrenceStream( int[] arr) {
        Arrays.stream(arr).boxed().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())).entrySet().parallelStream().filter(e -> e.getValue() % 2 != 0).forEach(entry -> System.out.println(entry.getKey()+"="+ entry.getValue()));
    }

    public static void calculateAllOddOccurrenceStream(List<Integer> list) {
        list.parallelStream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())).entrySet().parallelStream().filter(e -> e.getValue() % 2 != 0).forEach(entry -> System.out.println(entry.getKey()+"="+ entry.getValue()));
    }

    public static void main(final String... doYourBest) {

        final int[] arr = new int[200000000];

        generateData(arr);
        long starttime = System.currentTimeMillis();
        calculateAllOddOccurrence(arr);
        System.out.println("Total time with simple map=" + (System.currentTimeMillis() - starttime));

        List<Integer> list = Arrays.stream(arr).boxed().collect(Collectors.toList());
        starttime = System.currentTimeMillis();
        calculateAllOddOccurrenceStream(list);
        System.out.println("Total time stream - with a readymade list, which might be the case for most apps as arraylist is more easier to work with =" + (System.currentTimeMillis() - starttime));

        starttime = System.currentTimeMillis();
        calculateAllOddOccurrenceStream(arr);
        System.out.println("Total time Stream with array=" + (System.currentTimeMillis() - starttime));

    }}

这些线程似乎同时在阵列的相同部分上工作,因此答案不正确

而是使用适当的开始索引和结束索引将数组分成若干部分。分配单独的线程来处理这些部分,并统计每个部分中每个数字的出现次数

最后,您将有多个贴图,其中的计数是从这些单独的部分计算出来的。合并这些地图以获得最终答案


或者您可以使用一个concurrentHashMap来存储来自所有这些线程的计数,但是我想可能会有一个bug潜入其中,因为仍然会存在并发写入冲突。在高度多线程的环境中,cocnurrentHashMap上的写入可能不是100%安全的。对于有保证的写入行为,正确的方法是使用ConcurrentHashMap.putIfAbsentK键的原子性,V值方法,并注意返回值,该值指示put操作是否成功。简单的put可能不正确。看

您可以使用Java8StreamsAPI编写代码,也可以使用Java5构造编写简单的线程代码

添加了Java8流代码,请注意计时差异。ArrayList而不是数组会产生不同:

package com.test;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Test {

    public static void generateData(final int[] arr) {
        final Random aRandom = new Random();
        for (int i = 0; i < arr.length; i++) {
            arr[i] = aRandom.nextInt(10);
        }
    }

    public static void calculateAllOddOccurrence(final int[] arr) {
        final Map<Integer, Integer> mymap  = new HashMap<>();
        for (int i = 0; i < arr.length; i++) {
            if (mymap.containsKey(arr[i])) {
                mymap.put(arr[i], mymap.get(arr[i]) + 1);
            } else {
                mymap.put(arr[i], 1);
            }
        }
        for (final Map.Entry<Integer, Integer> entry : mymap.entrySet()) {
            if (entry.getValue() % 2 != 0) {
                System.out.println(entry.getKey() + "=" + entry.getValue());
            }

        }
    }

    public static void calculateAllOddOccurrenceStream( int[] arr) {
        Arrays.stream(arr).boxed().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())).entrySet().parallelStream().filter(e -> e.getValue() % 2 != 0).forEach(entry -> System.out.println(entry.getKey()+"="+ entry.getValue()));
    }

    public static void calculateAllOddOccurrenceStream(List<Integer> list) {
        list.parallelStream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())).entrySet().parallelStream().filter(e -> e.getValue() % 2 != 0).forEach(entry -> System.out.println(entry.getKey()+"="+ entry.getValue()));
    }

    public static void main(final String... doYourBest) {

        final int[] arr = new int[200000000];

        generateData(arr);
        long starttime = System.currentTimeMillis();
        calculateAllOddOccurrence(arr);
        System.out.println("Total time with simple map=" + (System.currentTimeMillis() - starttime));

        List<Integer> list = Arrays.stream(arr).boxed().collect(Collectors.toList());
        starttime = System.currentTimeMillis();
        calculateAllOddOccurrenceStream(list);
        System.out.println("Total time stream - with a readymade list, which might be the case for most apps as arraylist is more easier to work with =" + (System.currentTimeMillis() - starttime));

        starttime = System.currentTimeMillis();
        calculateAllOddOccurrenceStream(arr);
        System.out.println("Total time Stream with array=" + (System.currentTimeMillis() - starttime));

    }}

查看您的代码,您的这一行出现了错误:

mymap1.put(arr[i], mymap1.get(arr[i]) + 1);
您正在并行覆盖这些值,例如:

Thread 1 'get' = 0
Thread 2 'get' = 0
Thread 1 'put 1' 
Thread 2 'put 1'
将地图更改为:

static Map<Integer, AtomicInteger>       mymap1 = new ConcurrentHashMap<>();
static {
    //initialize to avoid null values and non-synchronized puts from different Threads
    for(int i=0;i<10;i++) {
        mymap1.put(i, new AtomicInteger());
    }
}
....
    //in your loop
    for (int i = 0; i < arr.length; i++) {
        AtomicInteger accumulator = mymap1.get(arr[i]);
        accumulator.incrementAndGet();
    }
编辑:上述方法的问题当然是mymap1的初始化。为了避免落入相同的陷阱,在循环中创建AtomicInteger并再次相互覆盖,需要使用值对其进行预填充

因为我觉得自己很慷慨,下面是使用Streams API的一些方法:

int totalEvenCount = Arrays.stream(arr).parallel().filter(i->i%2==0).reduce(0, Integer::sum);
int totalOddCount = Arrays.stream(arr).parallel().filter(i->i%2!=0).reduce(0, Integer::sum);

//or this to count by individual numbers:
ConcurrentMap<Integer,List<Integer>> map1 = Arrays.stream(arr).parallel().boxed().collect(Collectors.groupingByConcurrent(i->i));
map1.entrySet().stream().filter(e -> e.getKey()%2!=0).forEach(entry -> System.out.println(entry.getKey() + "=" + entry.getValue().size()));

作为读者的练习,也许您可以了解各种收集器是如何工作的,以便编写自己的countingByi->i%2=0以输出仅包含计数而不是值列表的映射。

查看代码,这一行出现错误:

mymap1.put(arr[i], mymap1.get(arr[i]) + 1);
您正在并行覆盖这些值,例如:

Thread 1 'get' = 0
Thread 2 'get' = 0
Thread 1 'put 1' 
Thread 2 'put 1'
将地图更改为:

static Map<Integer, AtomicInteger>       mymap1 = new ConcurrentHashMap<>();
static {
    //initialize to avoid null values and non-synchronized puts from different Threads
    for(int i=0;i<10;i++) {
        mymap1.put(i, new AtomicInteger());
    }
}
....
    //in your loop
    for (int i = 0; i < arr.length; i++) {
        AtomicInteger accumulator = mymap1.get(arr[i]);
        accumulator.incrementAndGet();
    }
编辑:上述方法的问题当然是mymap1的初始化。为了避免落入相同的陷阱,在循环中创建AtomicInteger并再次相互覆盖,需要使用值对其进行预填充

因为我觉得自己很慷慨,下面是使用Streams API的一些方法:

int totalEvenCount = Arrays.stream(arr).parallel().filter(i->i%2==0).reduce(0, Integer::sum);
int totalOddCount = Arrays.stream(arr).parallel().filter(i->i%2!=0).reduce(0, Integer::sum);

//or this to count by individual numbers:
ConcurrentMap<Integer,List<Integer>> map1 = Arrays.stream(arr).parallel().boxed().collect(Collectors.groupingByConcurrent(i->i));
map1.entrySet().stream().filter(e -> e.getKey()%2!=0).forEach(entry -> System.out.println(entry.getKey() + "=" + entry.getValue().size()));

作为读者的练习,也许您可以了解各种收集器是如何工作的,以便编写自己的countingByi->i%2=0以输出仅包含计数而不是值列表的映射。

您考虑过Java 8+中的流API吗?是的,当然,无法通过它解决此问题。你能帮忙吗?如果你向我展示你现有的基于Streams API的代码,我会帮你的。但是,作为错误所在的提示,您正在并行地覆盖循环中map1中的值。使用ConcurrentHashMap d

oesn不能改变last put获胜的事实。您考虑过Java 8+中的流API吗?是的,当然,无法通过它解决这个问题。你能帮忙吗?如果你向我展示你现有的基于Streams API的代码,我会帮你的。但是,作为错误所在的提示,您正在并行地覆盖循环中map1中的值。使用ConcurrentHashMap不会改变last put获胜的事实。注意:为链接提供上下文-鼓励链接到外部资源,但请在链接周围添加上下文,以便您的其他用户了解它是什么以及为什么存在。始终引用重要链接的最相关部分,以防目标站点无法访问或永久脱机。注意:为链接提供上下文-鼓励链接到外部资源,但请在链接周围添加上下文,以便您的其他用户了解它是什么以及为什么存在。始终引用重要链接中最相关的部分,以防目标站点无法访问或永久脱机。但我想可能会有bug潜入其中,因为仍然会有并发写入冲突。-什么ConcurrentHashMap的要点是它是线程安全的,可以由多个线程同时写入。在高度多线程的环境中,在cocnurrentHashMap上写入可能不是100%安全的。对于有保证的写入行为,正确的方法是使用ConcurrentHashMap.putIfAbsentK键的原子性,V值方法,并注意返回值,该值指示put操作是否成功。简单的put可能不正确。看到“bug”了吧,get-then-put序列从来都不是原子的。它总是需要外部同步。好的,很好。很高兴更新答案以便更清楚。我已经更新了我的答案,如果有帮助,请升级投票并接受。但我想可能会出现错误,因为仍然会有并发写入冲突。-什么ConcurrentHashMap的要点是它是线程安全的,可以由多个线程同时写入。在高度多线程的环境中,在cocnurrentHashMap上写入可能不是100%安全的。对于有保证的写入行为,正确的方法是使用ConcurrentHashMap.putIfAbsentK键的原子性,V值方法,并注意返回值,该值指示put操作是否成功。简单的put可能不正确。看到“bug”了吧,get-then-put序列从来都不是原子的。它总是需要外部同步。好的,很好。很高兴更新答案以便更清楚。我已经更新了我的答案,如果有帮助,请投票并接受。谢谢你的错误。。已经编辑了程序,现在顺序和并行输出都是相同的。但是,并行执行所需的时间是原来的3倍多:已经添加了一些Streams API代码供您尝试并从中获得灵感。感谢您的回答。我现在明白你的方法了。它的清洁和功能。然而,它比命令式的方式慢。你知道y吗?@Dhananjay作为猜测,很可能是因为与Lamndas相关的开销。lamnda本身就是对象。编译器在编译时可以很容易地优化函数循环,但Lamnda对象创建/调用周期却不那么容易。这也取决于您如何进行计算——请注意,在我的代码中使用了boxed,它将所有整数转换为整数——这是一个很大的开销!您可以将数组创建为Integer[],并在测试中查看它的性能,因为将int与Integer进行比较是不公平的。感谢您的bug。。已经编辑了程序,现在顺序和并行输出都是相同的。但是,并行执行所需的时间是原来的3倍多:已经添加了一些Streams API代码供您尝试并从中获得灵感。感谢您的回答。我现在明白你的方法了。它的清洁和功能。然而,它比命令式的方式慢。你知道y吗?@Dhananjay作为猜测,很可能是因为与Lamndas相关的开销。lamnda本身就是对象。编译器在编译时可以很容易地优化函数循环,但Lamnda对象创建/调用周期却不那么容易。这也取决于您如何进行计算——请注意,在我的代码中使用了boxed,它将所有整数转换为整数——这是一个很大的开销!您可以将数组创建为Integer[],并查看它在测试中的性能,因为将int与Integer进行比较是不公平的。