ArrayList线程安全问题分析

测试代码:

import java.util.ArrayList;

public class TestThreadSafe {
    static final int LOOP_NUM = 10;
    public static void main(String[] args) throws InterruptedException {
        ThreadSafeSubClass test = new ThreadSafeSubClass();
        test.method1(LOOP_NUM);
    }
}
class ThreadSafe {
    int i = 0;
    public final void method1(int loopNumber) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < loopNumber; i++) {
            method2(list);
            method3(list);
        }
    }
    private void method2(ArrayList<String> list) {
        list.add("1");
        System.out.println("add-"+ (i++));
    }
    public void method3(ArrayList<String> list) {
        list.remove(0);
    }
}
class ThreadSafeSubClass extends ThreadSafe{
    @Override
    public void method3(ArrayList<String> list) {
        new Thread(() -> {
            list.remove(0);
            System.out.println(Thread.currentThread().getName() + "removed--" + list.size());
        }).start();
    }
}

出现异常:

Exception in thread "Thread-9" java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
	at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:266)
	at java.base/java.util.Objects.checkIndex(Objects.java:359)
	at java.base/java.util.ArrayList.remove(ArrayList.java:504)
	at org.timqiu.ThreadSafeSubClass.lambda$method3$0(TestThreadSafe.java:36)
	at java.base/java.lang.Thread.run(Thread.java:833)

原因分析:

既然是list.remove(0)中的Objects.checkIndex()出现了问题,我们就进源码看一看:

private void add(E e, Object[] elementData, int s) {
    if (s == elementData.length)
        elementData = grow();
    elementData[s] = e;
    size = s + 1;  //每一次add,size都会加1
} 
public E remove(int index) {
    // 根据异常信息,index和size都是0才会导致检查不通过,为什么呢?接着往下看
    Objects.checkIndex(index, size);	
    final Object[] es = elementData;

    @SuppressWarnings("unchecked") E oldValue = (E) es[index];
    //这个方法修改了size
    fastRemove(es, index);

    return oldValue;
}

private void fastRemove(Object[] es, int i) {
    modCount++;
    final int newSize;
    // 注意这里
    if ((newSize = size - 1) > i)
        System.arraycopy(es, i + 1, es, i, newSize - i);
    es[size = newSize] = null;
}

复现问题步骤:

1、main线程执行method1,进入for循环

2、main线程执行method2中的add方法,ArrayList中的size为1

3、main线程执行method3方法,创建一个remove线程,我们暂且称之为T1线程,但这个T1线程执行到fastRemove()方法中的System.arraycopy()就停下了,现在的size为1,所以newsize=size-1=0,此时CPU将时间片分给main线程

4、main线程进入第二轮for循环,又执行了一遍method2中的add方法,此时ArrayList中的size为2

5、T1线程恢复执行,将值为0的newsize赋给size,此时ArrayList中的size为0!!!

6、main线程循环继续执行method3,创建了一个T2线程,CPU将时间片让给T2线程执行

7、问题来了,T2线程进入remove方法,但此时的ArrayList的size是0,传进来的index也为0,Objects.checkIndex()一看,直接就报了个异常!

posted @   TimQiu  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示