다 중 스 레 드 에서 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 의 값 을 늘 려 야 한다 면..
  • element Data[Size]의 위치 에 이 요 소 를 저장 합 니 다

  • 다음은 예시 의 세 가지 상황 에 대해 하나씩 분석한다.
    단일 스 레 드 가 실 행 된 경우 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 이 나타 나 는 지 설명 한다.

    좋은 웹페이지 즐겨찾기