排序时遇到的奇怪异常 Comparison method violates its general contract!

内容纲要

背景

话说某次发布新版本后,在日志里发现了奇怪的异常

Comparison method violates its general contract!
java.lang.IllegalArgumentException: Comparison method violates its general contract!
    at java.util.ComparableTimSort.mergeLo(ComparableTimSort.java:744)
    at java.util.ComparableTimSort.mergeAt(ComparableTimSort.java:481)
    at java.util.ComparableTimSort.mergeForceCollapse(ComparableTimSort.java:422)
    at java.util.ComparableTimSort.sort(ComparableTimSort.java:222)
    at java.util.Arrays.sort(Arrays.java:1312)
    at java.util.Arrays.sort(Arrays.java:1506)
    at java.util.ArrayList.sort(ArrayList.java:1462)
    at java.util.Collections.sort(Collections.java:141)

做了这么多年开发,还是头一次遇到这个异常,貌似是排序时抛出来的,看了代码也没有什么头绪,网上搜索了一番,发现这个异常造成了比较广泛的迷惑

异常原因

根据搜索结果,有以下几种观点

  • list 里有 null 对象,这个被我排除了,null 对象是抛出 NPE
  • 进行比较时未考虑相等的情况
  • 比较结果不能传递,比如, a > bb > c,那么应该得出 a > c,如果实际比较逻辑不能支持这个传递性,会抛出这个异常
  • 相等的对象,比较时区分出了大小,比如,a.equals(b) == true,比较时发生 a > b 或者 a < b

后两种说法好像很有道理,我对着我的 compareTo 代码研究了很久,也构造了一些测试用例,但结果都无法重现这个异常

解决办法

最后,我发现是线程安全的问题

我排序的对象是从缓存里取出来的,然后给对象的某几个属性赋值,排序则基于上一步骤赋值的那几个属性。在多线程的情况下,同一个对象可能被多个线程并发赋值,这造成排序时的分数并不固定。

最终的解决办法就是从缓存里取出对象后,复制一份再赋值排序,这样就不再有异常了

排序时遇到的奇怪异常 Comparison method violates its general contract!

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

Scroll to top
粤ICP备2020114259号 粤公网安备44030402004258