测试代码:
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()一看,直接就报了个异常!
标签:java,int,ArrayList,list,安全,线程,size From: https://www.cnblogs.com/timqiu/p/17764344.html