ArrayList并发add()可能出现数组下标越界异常 | 10+10<20

来源:互联网 发布:淘宝网怎么找人工客服 编辑:程序博客网 时间:2024/05/20 20:43

假设都知道在ArrayList内部add()的实现过程了。不知道我们就详细回顾一下,分两步操作,step1检查array容量,step2塞值并将size++。

——数组下标越界

假设有2个线程操作同一个ArrayList al,且array的容量刚好还可以存一个值。Thread1执行add()的step1完成,step2还没开始时被挂起,这时Thread1“认为”array还有位置可以塞值,然后安心地睡去了;Thread2执行add(),存入一个元素并将size++,而+1后的size已经大于array当前容量了。然后Thread1醒过来往下执行,因为它睡前已经检查过array容量了,所以就直接执行step2,直接把值存在下标为size的地方,所以就越界了。

——10+10<20(两个线程各add10个元素,最后size<20)

假设有2个线程操作同一个ArrayList。情形一:Thread1执行add()到step2的塞值完成但size++未开始时被挂起;Thread2执行add(),存入一个元素并将size++。问题来了,由于Thread1塞值后未将size+1,所以Thread2存入元素的位置跟Thread1存的位置时一样的,Thread1存的值被覆盖了。然后Thread1将size+1。这种情况size的一致性没有被破坏,但array有一个位置出现null的情况。情形二:如果前面Thread1被挂起前(已经取到了size的值只是还没+1 |  已经取到了size的值并+1但还没写回al),即size++完成了一半(java自增非原子操作),那么Thread1在醒来之后,size实际上已经过期了,这时(+1再 | 直接)赋值给al的size字段(属性),size值就跟预期的不一样了。当然,如果没有其他问题的话,size值跟array实际存的元素的个数是对应的上的,只是中间有些应该存的数据丢失了而已。


关键:

1、Thread1被挂起那一瞬间执行的位置不一样;

2、size是两个线程公有变量;

3、自增是非原子操作;

4、不可描述...

(已经取到了size的值只是还没+1 |  已经取到了size的值并+1但还没写回al) ,不知道自增拆分成哪几个原子操作的,回头知道了再来改。

完。

阅读全文
0 0