问题背景
Java集合相信大家并不陌生,下面我会总结最近遇到的一个线上问题并对Java排序做一个总结。问题的背景如下:List表示人群规则集合,为了减少对下游的调用量,先是对List做一个排序,然后以人群集合+用户id做为key进行缓存,以下面代码为例。
//并发服务框架
private static ExecutorService executorService = Executors.newFixedThreadPool(8);
//共享变量
private static List<String> list = Lists.newArrayList("1", "2");
private static void test() {
for (int i = 0; i < 100; i++) {
executorService.execute(new Runnable() {
public void run() {
Collections.sort(list);
//doSomething
}
});
}
}
业务上线后并没有出现过问题,但是有天晚上线上大量报错,异常如下:
并发对共享资源的访问产生异常,问题出在下面这行。简单讲,ArrayList不是线程安全的集合,在并发做“写”操作时,会产生线程安全问题。
Collections.sort(list);
下面是ArrayList排序的代码逻辑。
@Override
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
解决思路也是比较简单,拷贝一个局部变量,然后使用该变量去做排序,代码逻辑如下:
private static void test2() {
for (int i = 0; i < 100; i++) {
executorService.execute(new Runnable() {
public void run() {
List<String> copyList = Lists.newArrayList(list);
Collections.sort(copyList);
}
});
}
}
当时是用上面这种办法解决的,后来看到几种其它安全处理的方式,比如:
//并发服务框架
private static ExecutorService executorService = Executors.newFixedThreadPool(8);
private static Vector<String> vector = new Vector<String>();
{
vector.add("1");
vector.add("2");
}
private static void test3() {
for (int i = 0; i < 100; i++) {
executorService.execute(new Runnable() {
public void run() {
Collections.sort(vector);
}
});
}
}