다 중 스 레 드 에서 ArrayList 가 Add()요 소 를 추가 할 때 아래 표 시 된 크로스 오 버 문제(java.lang.Array IndexOutOfBounds Exception)를 호출 합 니 다.
16105 단어 Java
다 중 스 레 드 에서 ArrayList 를 사용 하여 Add()요 소 를 호출 할 때 다음 오류 가 발생 할 수 있 습 니 다.
Exception in thread "Thread-1" Exception in thread "Thread-2" java.lang.ArrayIndexOutOfBoundsException: 15
at java.util.ArrayList.elementData(ArrayList.java:418)
at java.util.ArrayList.get(ArrayList.java:431)
at com.jant.demo_jant.DemoJantApplication$AddToTest.run(DemoJantApplication.java:35)
at java.lang.Thread.run(Thread.java:745)
java.lang.ArrayIndexOutOfBoundsException: 15
at java.util.ArrayList.add(ArrayList.java:459)
at com.jant.demo_jant.DemoJantApplication$AddToTest.run(DemoJantApplication.java:32)
at java.lang.Thread.run(Thread.java:745)
그렇다면 문제 가 생 겼 다.Array List 는 자동 으로 확장 되 고 길이 제한 이 없 는데 왜 배열 아래 표 시 를 넘 는 오류 가 발생 했 을 까?
예제 코드:
이 예 를 통 해 연구 해 보 자.
public class DemoJantApplication {
public static List numberlist = new ArrayList<>();
class AddToTest implements Runnable {
int startNum = 0;
public AddToTest(int startNum) {
this.startNum = startNum;
}
@Override
public void run() {
int count = 0;
while (count < 100) {
try {
java.lang.Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
numberlist.add(startNum);
System.out.println(java.lang.Thread.currentThread().getName() + "--" + (count) + "times"
+" :"+ startNum
+ ",list :" + numberlist.get(numberlist.size() - 1)
+ ", :" + numberlist.size()
+ ",list" + numberlist.toString());
startNum += 2;
count++;
}
}
}
public static void main(String[] args) {
Thread t = new Thread(new DemoJantApplication().new AddToTest(0));
Thread t1 = new Thread(new DemoJantApplication().new AddToTest(100));
t.start();
t1.start();
}
}
인쇄 된 log,나 는 그 를 세 가지 상황 으로 나 누 었 다.
상황 1:
이런 상황 은 정상 적 인 상황 이 라 고 할 수 있다.왜냐하면 원 소 를 잃 어 버 리 지 않 았 기 때문이다.
스 레 드 1 과 스 레 드 2,0 차 실행 되 지만 집합 크기 는 모두 2 입 니 다.
Thread-1--0times :0,list :100, :2,list[0, 100]
Thread-2--0times :100,list :100, :2,list[0, 100]
Thread-1--1times :2,list :2, :4,list[0, 100, 102, 2]
Thread-2--1times :102,list :2, :4,list[0, 100, 102, 2]
Thread-1--2times :4,list :4, :5,list[0, 100, 102, 2, 4]
상황 2:
스 레 드 1 과 스 레 드 2,0 차 실행 되 지만 집합 크기 는 모두 1 입 니 다.
Thread-1--0times :0,list :100, :1,list[100]
Thread-2--0times :100,list :100, :1,list[100]
Thread-1--1times :2,list :2, :2,list[100, 2]
Thread-2--1times :102,list :102, :3,list[100, 2, 102]
Thread-1--2times :4,list :104, :4,list[100, 2, 102, 104]
상황 3:
요소 추가 오류
Exception in thread "Thread-1" Exception in thread "Thread-2" java.lang.ArrayIndexOutOfBoundsException: 15
at java.util.ArrayList.elementData(ArrayList.java:418)
at java.util.ArrayList.get(ArrayList.java:431)
at com.jant.demo_jant.DemoJantApplication$AddToTest.run(DemoJantApplication.java:35)
at java.lang.Thread.run(Thread.java:745)
java.lang.ArrayIndexOutOfBoundsException: 15
at java.util.ArrayList.add(ArrayList.java:459)
at com.jant.demo_jant.DemoJantApplication$AddToTest.run(DemoJantApplication.java:32)
at java.lang.Thread.run(Thread.java:745)
분석 하 다.
우선,Array List 는 배열 을 바탕 으로 이 루어 진 것 으로 동적 배열 로 그 용량 은 자동 으로 증가 할 수 있 으 며 C 언어의 동적 신청 메모리 와 유사 하 며 동적 성장 메모리 와 유사 하 다.
ArrayList 의 경우 List 인터페이스,바 텀 에서 배열 을 사용 하여 모든 요 소 를 저장 합 니 다.그 조작 은 기본적으로 배열 에 대한 조작 이다.
먼저 Array List 에서 add()를 사용 하여 요 소 를 추가 하 는 절 차 를 살 펴 보 겠 습 니 다.
1.요 소 를 추가 합 니 다.그 중에서 size 는 element Data 배열 의 메타 그룹의 개수 이 고 초기 에는 0 입 니 다.
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
2.list 의 용량 을 확정 하고 그 역할 은 배열 의 용량 이 항상 충분 하도록 확보 하 는 것 이다.
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//DEFAULT_CAPACITY 10
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
3.list 의 길 이 를 확장 하고 매번 배열 용량 의 증 가 는 원 용량 의 1.5 배 이다.
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// ,
elementData = Arrays.copyOf(elementData, newCapacity);
}
배열 을 확장 할 때,오래된 배열 의 요 소 를 새로운 배열 로 복사 합 니 다.
4.다음 에 Add()함수 로 돌아 가 계속 실행 합 니 다.element Data[size+]=e;
원 소 를 추가 할 때 두 단계 로 완 성 될 수 있 습 니 다.
다음은 예시 의 세 가지 상황 에 대해 하나씩 분석한다.
단일 스 레 드 가 실 행 된 경우 Size=0 이면 하나의 요 소 를 추가 한 후 이 요 소 는 위치 0 이 고 Size=1 입 니 다.
상황 1:
실행 순서,다 중 스 레 드 상황 에서
스 레 드 A 는 ArrayList 에 요소,위치 0 을 추가 합 니 다.
스 레 드 B 는 ArrayList 에 요소,위치 1 을 추가 합 니 다.
스 레 드 A 실행 인쇄 코드
스 레 드 B 실행 인쇄 코드
상황 2:
다 중 스 레 드 상황 이 라면,
1.처음에 ArrayList 크기 는 0 이 었 고 처음으로 크기 를 10(size 를 통 해 얻 은 것 은 실제 저 장 된 데이터 의 크기)으로 확 장 했 습 니 다.2.스 레 드 A 에 요 소 를 추 가 했 는데 이때 size 는 0 입 니 다.element Data[0]=e 실행 준비 중;이후 size++를 실행 할 때 CPU 스 케 쥴 링 스 레 드 A 일시 정지 3,스 레 드 B 에 요 소 를 추가 합 니 다.이때 size 는 0 이 고 element Data[0]=e 를 실 행 했 습 니 다.그 다음 에 size++를 실행 합 니 다.이때 size 는 1.4 입 니 다.이때 스 레 드 A 는 size+를 계속 실행 합 니 다.이때 size 는 1 입 니 다.
그래서 스 레 드 B 에 추 가 된 값 은 스 레 드 A 에 추 가 된 값 으로 덮어 씁 니 다.
상황 3:
월경 이 발생 했 을 때의 수조 하 표 는 각각 10,15,22,33,49 와 73 이다.앞에서 말 한 배열 자동 체 제 를 결합 하여 배열 의 초기 길 이 는 10 이 고 첫 번 째 확장 용량 은 15=10+10/2 이 며 두 번 째 확장 용량 은 22=15+15/2 이 며 세 번 째 확장 용량 은 33=22+22/2 이다.이런 추 세 를 통 해 계속 디 버 깅 을 통 해 알 수 있 듯 이 크로스 오 버 이상 은 모두 배열 이 확대 할 때 발생 한 다 는 것 을 알 수 있다.
1.집합 에 14 개의 요 소 를 추 가 했 을 때 스 레 드 A 가 먼저 add()방법 에 들 어 갔 고 ensureCapacity Internal(size+1)을 실 행 했 을 때 하나의 요 소 를 추가 할 수 있 음 을 발 견 했 기 때문에 배열 은 확장 되 지 않 았 으 나 그 후에 스 레 드 A 가 여기 서 막 혔 다.
2.이 어 스 레 드 B 가 add()방법 에 들 어가 서 ensureCapacity Internal(size+1)을 실행 합 니 다.스 레 드 A 가 요 소 를 추가 하지 않 았 기 때문에 size 는 14 이 고 확장 이 필요 하지 않 습 니 다.그래서 이 스 레 드 는 요 소 를 추가 하기 시 작 했 습 니 다.size+를 15 로 바 꾸 었 고 배열 이 가득 찼 습 니 다.
3.이때 스 레 드 A 가 실행 되 고 element Data[size+]=e 를 실행 합 니 다.집합 에 16 번 째 요 소 를 추가 해 야 하 는데 배열 의 용량 은 15 개 밖 에 없 기 때문에 배열 아래 표 시 된 경계 이상 이 발생 합 니 다!
참고:
ArrayList 가 다 중 스 레 드 에서 Add()요 소 를 호출 할 때 아래 표 시 된 크로스 오 버 문제(java.lang.Array Index OutOf Bounds Exception)
이 블 로 그 는 잘 썼 지만 다음 단락 의 분석 은 정확 하지 않다.
만약 에 다 중 스 레 드 상황 에서 예 를 들 어 두 개의 스 레 드 가 있 으 면 스 레 드 A 는 먼저 요 소 를 위치 0 에 저장 합 니 다.그러나 이때 CPU 스케줄 러 A 가 일시 정지 되 고 스 레 드 B 가 실 행 될 기 회 를 얻 습 니 다.스 레 드 B 도 이 ArrayList 에 요 소 를 추가 합 니 다.이때 Size 는 0 과 같 기 때 문 입 니 다.(주의 하 세 요.우 리 는 하나의 요 소 를 추가 하 는 것 은 두 단계 가 필요 하 다 고 가정 합 니 다.스 레 드 A 는 절차 1 만 완 료 했 기 때문에 스 레 드 B 도 요 소 를 위치 0 에 저장 합 니 다.그 다음 에 스 레 드 A 와 스 레 드 B 가 계속 실행 되 고 모두 Size 의 값 을 증가 합 니 다.좋 습 니 다.Array List 의 상황 을 살 펴 보 겠 습 니 다.요 소 는 실제로 하나 밖 에 없 는데 위치 0 에 저장 되 어 있 지만 Size 는 2 와 같 습 니 다.이것 이 바로'라인 이 안전 하지 않다'는 것 이다.집합 중 에 왜 null 이 나타 나 는 지 설명 한다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JPA + QueryDSL 계층형 댓글, 대댓글 구현(2)이번엔 전편에 이어서 계층형 댓글, 대댓글을 다시 리팩토링해볼 예정이다. 이전 게시글에서는 계층형 댓글, 대댓글을 구현은 되었지만 N+1 문제가 있었다. 이번에는 그 N+1 문제를 해결해 볼 것이다. 위의 로직은 이...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.