当前位置: 首页 > 科技观察

不要在线上滥用CopyOnWriteArrayList,姿势不对性能真的很糟糕

时间:2023-03-12 03:01:22 科技观察

网上不要滥用CopyOnWriteArrayList,姿势不对性能真的很差,不是修改数据,而是复制数据,对复制的数组进行操作。通过这样的机制,可以大大提高读的并发性能,所以对于CopyOnWriteArrayList来说,非常适合读多写少的场景或者无锁的场景。但是,如果为了炫技而不分场合地滥用CopyOnWriteArrayList,可能会适得其反。接下来我们用一段测试代码来比较CopyOnWriteArrayList和普通加锁ArrayList的读写性能差距。我们先来测试写入性能的差距:搭建一个CopyOnWriteArrayList和synchronizedList,通过多线程并发写入10万个元素。ListcopyOnWriteArrayList=newCopyOnWriteArrayList<>();//建立一个锁定的ListListsynchronizedList=Collections.synchronizedList(newArrayList<>());StopWatchstopWatch=newStopWatch();intloopCount=100000;stopWatch.start("测试写入性能:copyOnWriteArrayList");//多线程写入100000个数IntStream.rangeClosed(1,loopCount).parallel().forEach(x->copyOnWriteArrayList.add(ThreadLocalRandom.current().nextInt(loopCount)));stopWatch.stop();stopWatch.start("测试写入性能:synchronizedList");//多线程写入100000个数IntStream.rangeClosed(1,loopCount).parallel().forEach(x->synchronizedList.add(ThreadLocalRandom.current().nextInt(loopCount)));stopWatch.stop();System.out.println(stopWatch.prettyPrint());Mapresult=newHashMap<>();result.put("copyOnWriteArrayList",copyOnWriteArrayList.size());result.put("synchronizedList",synchronizedList.size());System.out.println(JSON.toJSONString(result));可以明显看出,在大写量的情况下,CopyOnWriteArrayList的性能远不如普通的加锁List,性能差距可能在100倍以上。CopyOnWriteArrayList之所以写的这么慢,是因为CopyOnWriteArrayList写的时候需要复制存放元素的旧数组来创建新的数组,导致内存申请和释放消耗很大。下面来测试一下大量读取的性能差距:先向两个List写入10万个元素,然后通过多线程随机获取元素。ListsynchronizedList=Collections.synchronizedList(newArrayList<>());synchronizedList.addAll(IntStream.rangeClosed(1,100000).boxed().collect(Collectors.toList()));ListcopyOnWriteArrayList=IntStream.rangeClosed(1,100000).boxed().collect(Collectors.toCollection(CopyOnWriteArrayList::new));StopWatchstopWatch=newStopWatch();intloopCount=1000000;intcount=copyOnWriteArrayList.size();stopWatch.start("测试读取能力:copyOnWriteArrayList");IntStream.rangeClosed(1,loopCount).parallel().forEach(x->copyOnWriteArrayList.get(ThreadLocalRandom.current().nextInt(count)));stopWatch.stop();stopWatch.start("测试读取能力:synchronizedList");IntStream.range(0,loopCount).parallel().forEach(x->synchronizedList.get(ThreadLocalRandom.current().nextInt(count)));stopWatch.stop();System.out.println(stopWatch.prettyPrint());Mapresult=newHashMap<>();result.put("copyOnWriteArrayList",copyOnWriteArrayList.size());result.put("synchronizedList",synchronizedList.size());System.out.println(JSON.toJSONString(结果));经过多次测试,CopyOnWriteArrayList的读取性能大概在正常锁CopyOnWriteArrayList的读取是List的2-5倍左右,而CopyOnWriteArrayList读取快的原因是CopyOnWriteArrayList读取的元素是直接通过处于无锁状态的数组下标。一般来说,CopyOnWriteArrayList只适用于读取量大的场景。如果有大量写操作,性能不如普通List。JDK为我们提供了很多针对并发场景的工具类,但是我们还是需要仔细了解每个工具的使用场景。在不合适的场景中使用不合适的工具会导致性能下降。