Java 排序并行流时遇到顺序错误

Java 排序并行流时遇到顺序错误,java,parallel-processing,java-8,java-stream,Java,Parallel Processing,Java 8,Java Stream,我有一个记录课程: public class Record implements Comparable<Record> { private String myCategory1; private int myCategory2; private String myCategory3; private String myCategory4; private int myValue1; private double myValue2;

我有一个
记录
课程:

public class Record implements Comparable<Record>
{
   private String myCategory1;
   private int    myCategory2;
   private String myCategory3;
   private String myCategory4;
   private int    myValue1;
   private double myValue2;

   public Record(String category1, int category2, String category3, String category4,
      int value1, double value2)
   {
      myCategory1 = category1;
      myCategory2 = category2;
      myCategory3 = category3;
      myCategory4 = category4;
      myValue1 = value1;
      myValue2 = value2;
   }

   // Getters here
}
请注意,前10000条记录的
类别2
0
,然后下10000条记录的
1
,以此类推,而
value1
值依次为0-114999

我创建了一个
,它既
并行
排序

Stream<Record> stream = list.stream()
   .parallel()
   .sorted(
       //(r1, r2) -> Integer.compare(r1.getCategory2(), r2.getCategory2())
   )
   //.parallel()
;
Stream<Record> stream = list.stream().sorted().parallel();
我用这个把戏

压缩输出:

0
1
2
3
...
69996
69997
69998
69999
71875  // discontinuity!
71876
71877
71878
...
79058
79059
79060
79061
70000  // discontinuity!
70001
70002
70003
...
71871
71872
71873
71874
79062  // discontinuity!
79063
79064
79065
79066
...
114996
114997
114998
114999
output
size()
115000
,所有元素似乎都在那里,只是顺序略有不同

所以我写了一些检查代码,看看排序是否稳定。如果它是稳定的,那么所有的
value1
值都应该保持顺序。此代码验证订单,打印任何差异

int prev = -1;
boolean verified = true;
for (Record record : output)
{
    int curr = record.getValue1();
    if (prev != -1)
    {
        if (prev + 1 != curr)
        {
            System.out.println("Warning: " + prev + " followed by " + curr + "!");
            verified = false;
        }
    }
    prev = curr;
}
System.out.println("Verified: " + verified);
输出:

Warning: 69999 followed by 71875!
Warning: 79061 followed by 70000!
Warning: 71874 followed by 79062!
Warning: 99999 followed by 100625!
Warning: 107811 followed by 100000!
Warning: 100624 followed by 107812!
Verified: false
如果执行以下任一操作,此情况将持续存在:

true
true
false
  • ForkJoinPool
    替换为
    ThreadPoolExecutor

    ThreadPoolExecutor pool = new ThreadPoolExecutor(8, 8, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));
    
  • 调用
    parallel()
    在我调用
    sorted
    之后

    Stream<Record> stream = list.stream()
       .parallel()
       .sorted(
           //(r1, r2) -> Integer.compare(r1.getCategory2(), r2.getCategory2())
       )
       //.parallel()
    ;
    
    Stream<Record> stream = list.stream().sorted().parallel();
    
  • 使用
    比较器进行排序
    。请注意,此排序标准不同于我为
    可比
    接口定义的“自然”顺序,尽管从一开始就以顺序排列的结果开始,但结果应该仍然相同

    Stream<Record> stream = list.stream().parallel().sorted(
        (r1, r2) -> Integer.compare(r1.getCategory2(), r2.getCategory2())
    );
    
    我的Java版本是1.8.0_05。这种异常现象也存在,它似乎正在运行Java8U25

    更新

    在撰写本文时,我已经将JDK升级到最新版本1.8.0_45,问题没有改变

    问题

    结果
    列表中的记录顺序(
    输出
    )是否由于排序不稳定、遭遇顺序未保留或其他原因而出现顺序错误


    当我创建并行流并对其排序时,如何确保保留遭遇顺序?

    它看起来像
    数组。在某些情况下,并行排序不稳定。好的。流并行排序是根据
    数组实现的。parallelSort
    ,因此它也会影响流。下面是一个简化的示例:

    public class StableSortBug {
        static final int SIZE = 50_000;
    
        static class Record implements Comparable<Record> {
            final int sortVal;
            final int seqNum;
    
            Record(int i1, int i2) { sortVal = i1; seqNum = i2; }
    
            @Override
            public int compareTo(Record other) {
                return Integer.compare(this.sortVal, other.sortVal);
            }
        }
    
        static Record[] genArray() {
            Record[] array = new Record[SIZE];
            Arrays.setAll(array, i -> new Record(i / 10_000, i));
            return array;
        }
    
        static boolean verify(Record[] array) {
            return IntStream.range(1, array.length)
                            .allMatch(i -> array[i-1].seqNum + 1 == array[i].seqNum);
        }
    
        public static void main(String[] args) {
            Record[] array = genArray();
            System.out.println(verify(array));
            Arrays.sort(array);
            System.out.println(verify(array));
            Arrays.parallelSort(array);
            System.out.println(verify(array));
        }
    }
    
    当然,它应该打印三次
    true
    。这是在当前的JDK 9开发版本上。考虑到您已经尝试过的情况,如果它出现在迄今为止所有的JDK 8版本中,我不会感到惊讶。奇怪的是,减小大小或除数将改变行为。20000的大小和10000的除数是稳定的,50000的大小和1000的除数也是稳定的。问题似乎与比较相等与平行分割大小的足够大的值运行有关


    OpenJDK的问题涉及到这个bug。

    我会尝试制作最简单的程序来重现这个问题,在最新的JDK版本上运行它,如果它被重现,我会提交一个bug:排序应该是稳定的:它是这样记录的。(真、真、假)同样在Windows7(64),8u40上。@StefanZobel哦,是的,谢谢,我已将新错误作为旧错误的副本关闭。@StuartMarks感谢您确定此错误的根本原因。我们知道什么时候能修好吗?修复程序将应用于Java 8还是只应用于Java 9未来的开发?@rgetman抱歉,对此没有估计。就我个人而言,我建议将修复程序后移植到8-update发布行,但我不能保证是否会发生这种情况。
    Stream<Record> stream = list.parallelStream().sorted();
    
    Stream<Record> stream = list.stream().parallel().sorted(
        (r1, r2) -> Integer.compare(r1.getCategory2(), r2.getCategory2())
    );
    
    Verified: true
    
    public class StableSortBug {
        static final int SIZE = 50_000;
    
        static class Record implements Comparable<Record> {
            final int sortVal;
            final int seqNum;
    
            Record(int i1, int i2) { sortVal = i1; seqNum = i2; }
    
            @Override
            public int compareTo(Record other) {
                return Integer.compare(this.sortVal, other.sortVal);
            }
        }
    
        static Record[] genArray() {
            Record[] array = new Record[SIZE];
            Arrays.setAll(array, i -> new Record(i / 10_000, i));
            return array;
        }
    
        static boolean verify(Record[] array) {
            return IntStream.range(1, array.length)
                            .allMatch(i -> array[i-1].seqNum + 1 == array[i].seqNum);
        }
    
        public static void main(String[] args) {
            Record[] array = genArray();
            System.out.println(verify(array));
            Arrays.sort(array);
            System.out.println(verify(array));
            Arrays.parallelSort(array);
            System.out.println(verify(array));
        }
    }
    
    true
    true
    false