CopyOnWriteArrayList是并发包下的线程安全List,文档里也注明了是线程安全的。但研究源码后发现,这个线程安全是打折扣的。线程安全,需要处理读写和写写问题,因为没法保证只是读读,所以读写都要受并发影响,直观的,读写都要靠一把锁来排他其它操作。经典的实现逻辑,见Vector,get/add都加synchronized。
CopyOnWriteArrayList在并发逻辑上的问题是:去看源码会发现,add里的逻辑是拷贝原来数组(即新建更大容量数组),添加新数据,再覆盖成员变量数组,然后新元素才可见。逻辑多,并且拷贝数据相对来讲是耗时的重量操作。反观get源码,就直接从数组按index取元素,逻辑极简单,操作轻量。所以可设想出一种场景:用户调用add加入元素(用户视角元素已加入了),然后另一个线程里稍晚点去取这个元素,由于get操作比add快很多,虽然用户get在add后,但get在新数组覆盖原数组,新元素可见前就拿到了旧数组,那么此时用户看到的是我先add,再去get,但是get不到。合理吗?正常吗?
CopyOnWrite让我不禁联想起数据库的MVCC,也是分离读写思想提高读写并发效率。但db的分离读写一部分原因是要高性能并发,另一部分原因是事务实现。因为事务的原因,读不到最新数据是一种合理的现象:你想一个事务内保持一致,很显然事务的生命周期内别人是有时间合规的改变数据的,你要想保持一致,只能放弃数据最新的要求。但CopyOnWriteArrayList不涉及事务概念,纯粹为了并发,读端就失去了读取最新数据的能力。这个逻辑不知道该如何理解接受呢?