多线程数组问题
背景
一个统计查询的接口,几十个机构查询统计不同类型案件的数量,涉及到查询数据库表操作,每个机构都会有许多次查表操作。那么几十个机构循环逐个查询的效率极低,性能差。当数据量稍微多点就会出现502超时错误。如何解决这个问题呢?
解决方案
案件对应的机构是唯一的,并且统计的结果中,相互直接不会有影响。那么就可以使用多线程的方式,分批处理机构,而不用单线程低效循环。
开始使用
// 代码待定
出现问题
- 出现数据被覆盖问题,即计算后总机构与原机构不同
- 出现空指针异常错误
出现这两个问题的原因就是未使用线程安全的集合。共享变量未保证一致性,线程安全集合即可解决这两个问题。那么具体分析这两个问题出现的原因。
覆盖问题
覆盖比较好理解,线程A占用索引一进行数据插入,在线程A未完全插入到集合的位置A时,线程B发现索引一未插入数据,则也开始在索引一插入数据,这时线程A完成了索引一位置的数据插入操作。线程B则也在索引一位置插入数据,导致线程A的数据被覆盖。
空指针问题
这里在使用List
的add()
方法时,会出现中间索引位置为null
的情况,那么怎么出现这个问题呢?个人理解,这里涉及到集合中游标的概念。当一个集合在遍历的时候,集合如何按照索引进行逐个获取集合的元素,游标在这里扮演重要作用,初始化集合游标指向索引0的位置,当遍历时,cursor++
的操作,移动游标的位置,查询获取每个索引对应的值。在add()
时,元素会先插入到当前游标指向的索引位置,然后再移动游标的位置,即往后移动一位。
在多线程的情况下,如果线程A在游标指向的索引为0的位置上插入数据前,线程B移动了游标的位置,这时,在线程A插入数据时就会把数据插入到索引为1的位置上,导致索引为0的位置无元素插入,即null
。
这么理解,索引比作一个一个的房间,每个房间都有具体的房间号,元素比作每个人。房间是固定的,也就是索引是固定不变的,而每个人怎么进入房间,就是游标要做的事情,游标指向哪个房间,人就该去哪个房间。总之,元素是跟随游标,指向索引位置。
总结
这两个问题最根本的原因是共享变量未保证一致性。
覆盖问题是在集合的索引这个共享变量出现竞争。
空指针问题是在集合的游标这个共享变量上出现竞争。