ArrayList 소스 코드 와 다 중 스 레 드 보안 문제 분석

1.ArrayList 소스 코드 와 다 중 스 레 드 안전 문제 분석
ArrayList 스 레 드 안전 문 제 를 분석 하기 전에 우 리 는 이러한 소스 코드 를 분석 하여 스 레 드 안전 문제 가 발생 할 수 있 는 곳 을 찾 은 다음 에 코드 를 검증 하고 분석 합 니 다.
1.1 데이터 구조
ArrayList 내 부 는 배열 로 요 소 를 저장 합 니 다.데이터 정 의 는 다음 과 같 습 니 다.

transient Object[] elementData; // non-private to simplify nested class access
Array List 에서 이 배열 은 자원 을 공유 하 는 것 입 니 다.다 중 스 레 드 가 이 데 이 터 를 조작 할 때 동기 화 통 제 를 하지 않 으 면 스 레 드 안전 문제 가 발생 할 수 있 습 니 다.
1.2 add 방법 에 나타 날 수 있 는 문제점 분석
우선 add 의 소스 코드 를 살 펴 보 겠 습 니 다.다음 과 같 습 니 다.

public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
이 방법 에는 두 가지 조작 이 있 는데 하 나 는 배열 용량 검사 이 고 다른 하 나 는 요 소 를 데이터 에 넣 는 것 이다.우 리 는 먼저 두 번 째 간단 한 분석 을 시작 합 니 다.여러 스 레 드 의 실행 순서 가 다음 과 같 을 때 최종 데이터 요소 의 개 수 는 기대 치보다 작 습 니 다.

이 순서대로 실 행 된 후에 우 리 는 element Data[n]의 값 이 두 번 만 설정 되 었 고 두 번 째 스 레 드 설정 의 값 은 이전 것 을 덮어 쓰 고 마지막 size=n+1 을 볼 수 있 습 니 다.다음은 코드 를 사용 하여 이 문 제 를 검증 합 니 다.
1.3 코드 검증
먼저 다음 코드 를 보고 1000 개의 스 레 드 를 열 고 ArrayList 의 add 방법 을 호출 합 니 다.각 스 레 드 는 ArrayList 에 100 개의 숫자 를 추가 합 니 다.프로그램 이 정상적으로 실 행 될 경우 출력 해 야 합 니 다.list size is :10000 코드 는 다음 과 같 습 니 다:

private static List<Integer> list = new ArrayList<Integer>();
private static ExecutorService executorService = Executors.newFixedThreadPool(1000);
private static class IncreaseTask extends Thread{
@Override
public void run() {
System.out.println("ThreadId:" + Thread.currentThread().getId() + " start!");
for(int i =0; i < 100; i++){
list.add(i);
}
System.out.println("ThreadId:" + Thread.currentThread().getId() + " finished!");
}
}
public static void main(String[] args){
for(int i=0; i < 1000; i++){
executorService.submit(new IncreaseTask());
}
executorService.shutdown();
while (!executorService.isTerminated()){
try {
Thread.sleep(1000*10);
}catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("All task finished!");
System.out.println("list size is :" + list.size());
}

이 main 방법 을 실행 하면 다음 과 같이 출력 합 니 다.

이상 의 실행 결 과 를 보면 마지막 출력 결 과 는 우리 의 기대 치보다 작 을 것 이다.즉,다 중 스 레 드 가 add 방법 을 호출 할 때 요소 가 덮어 쓰 는 문제 가 발생 합 니 다.
1.4 배열 용량 검사 의 병발 문제
add 방법 소스 코드 에서 우 리 는 요 소 를 추가 할 때마다 배열 용량 의 검 측 이 있 는 것 을 보 았 습 니 다.add 에서 이 방법 을 호출 하 는 소스 코드 는 다음 과 같 습 니 다.ensureCapacityInternal(size + 1);용량 검사 와 관련 된 소스 코드 는 다음 과 같 습 니 다.

private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
용량 검사 의 흐름 도 는 다음 과 같다.

우 리 는 두 개의 스 레 드 로 add 작업 을 수행 하여 확장 용량 에 발생 할 수 있 는 병발 문 제 를 분석 합 니 다.
Array List 를 새로 만 들 때 내부 배열 용기 의 용량 은 기본 용량 10 입 니 다.두 스 레 드 로 10 번 째 요 소 를 동시에 추가 할 때 다음 과 같은 실행 순서 가 나타 나 면 자바.lang.Array Index OutOf Bounds Exception 이상 을 던 질 수 있 습 니 다.

두 번 째 스 레 드 가 배열 에 데 이 터 를 추가 할 때 배열 의 용량 이 10 이기 때문에 이 작업 은 index 가 10 인 위치 에 요소 값 을 설정 하기 때문에 배열 의 경계 이상 을 던 집 니 다.
1.5 코드 검증 배열 용량 검사 의 병행 문제
다음 코드 사용:

private static List<Integer> list = new ArrayList<Integer>(3);
private static ExecutorService executorService = Executors.newFixedThreadPool(10000);
private static class IncreaseTask extends Thread{
@Override
public void run() {
System.out.println("ThreadId:" + Thread.currentThread().getId() + " start!");
for(int i =0; i < 1000000; i++){
list.add(i);
}
System.out.println("ThreadId:" + Thread.currentThread().getId() + " finished!");
}
}
public static void main(String[] args){
new IncreaseTask().start();
new IncreaseTask().start();
}
main 방법 을 실행 하면 콘 솔 출력 을 다음 과 같이 볼 수 있 습 니 다.

1.6 Array List 의 다른 방법 설명
Array List 에 공 유 된 변 수 를 조작 하 는 다른 방법 도 병행 안전 문제 가 있 을 수 있 으 므 로 상기 분석 방법 에 따라 분석 하면 된다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기