内容纲要
背景
话说某次发布新版本后,在日志里发现了奇怪的异常
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 > b
,b > c
,那么应该得出a > c
,如果实际比较逻辑不能支持这个传递性,会抛出这个异常 - 相等的对象,比较时区分出了大小,比如,
a.equals(b) == true
,比较时发生a > b
或者a < b
后两种说法好像很有道理,我对着我的 compareTo
代码研究了很久,也构造了一些测试用例,但结果都无法重现这个异常
解决办法
最后,我发现是线程安全的问题
我排序的对象是从缓存里取出来的,然后给对象的某几个属性赋值,排序则基于上一步骤赋值的那几个属性。在多线程的情况下,同一个对象可能被多个线程并发赋值,这造成排序时的分数并不固定。
最终的解决办法就是从缓存里取出对象后,复制一份再赋值排序,这样就不再有异常了
排序时遇到的奇怪异常 Comparison method violates its general contract!