자바 병렬 프로 그래 밍 (3) 스 레 드 보안 클래스 에 새로운 원자 작업 추가

4150 단어 자바
자바 라 이브 러 리 에는 실 용적 인 '기초 모듈' 클래스 가 많이 포함 되 어 있 습 니 다. 일반적으로 우 리 는 새로운 클래스 를 만 드 는 것 이 아니 라 기 존 클래스 를 재 활용 하 는 것 을 우선 선택해 야 합 니 다.: 재 활용 은 개발 작업량 을 줄 이 고 개발 위험 을 줄 일 수 있 습 니 다. (기 존 클래스 가 테스트 를 통 과 했 기 때 문 입 니 다)그리고 유지 비용. 어떤 스 레 드 안전 류 는 우리 가 필요 로 하 는 모든 작업 을 지원 할 수 있 지만, 많은 다른 경우, 기 존의 클래스 는 대부분의 작업 만 지원 할 수 있 습 니 다. 이 때 는 스 레 드 안전 을 파괴 하지 않 고 새로운 작업 을 추가 해 야 합 니 다.
        만약 우리 가 안전 한 링크 를 원한 다 면, 그 는 원자의 '없 으 면 (Put - If - Absent)' 동작 을 제공 해 야 합 니 다. 동기 화 된 List 류 는 이미 대부분의 기능 을 실현 하 였 으 며, 우 리 는 그것 이 제공 하 는 contains 방법 과 add 방법 에 따라 구 조 를 실현 할 수 있 습 니 다.
이 원자 조작 을 실현 하 는 네 가지 방법 이 있 을 수 있다.
첫 번 째 방법 이자 가장 안전 한 방법 은 바로 원시 류 를 바 꾸 는 것 이다.
그러나 이것 은 일반적으로 할 수 없습니다. 클래스 의 원본 코드 에 접근 하거나 변경 할 수 없 기 때 문 입 니 다. 원본 클래스 를 바 꾸 려 면 코드 의 동기 화 전략 을 깊이 이해 해 야 합 니 다. 이렇게 추 가 된 기능 은 기 존의 디자인 과 일치 합 니 다. 새로운 방법 을 클래스 에 직접 추가 한다 고 가정 하면 동기 화 정책 을 실현 하 는 모든 코드 가 원본 파일 에 있 음 을 의미 합 니 다.더 쉽게 이해 하고 유지 할 수 있 습 니 다.
또 다른 방법 은 이러한 종 류 를 확장 (계승) 할 수 있 습 니 다. - 만약 원시 류 가 디자인 할 때 확장 성 을 고려 했다 면.
예 를 들 어, 우 리 는 BetterVector 를 설계 하여 Vector 를 확장 할 수 있 고, 새로운 방법 인 putIfAbsent 를 추가 할 수 있다.
public class BetterVector<E> extends Vector<E>{
	public synchronized boolean putIfAbsent(E x){
		boolean absent = !contains(x);
		if(absent)add(x);
		return absent;
	}
}

        확장 Vector 는 매우 easy 이지 만 모든 클래스 가 Vector 처럼 상 태 를 하위 클래스 에 공개 하 는 것 은 아니 기 때문에 이런 방법 을 사용 하기에 적합 하지 않다.
        ”확장 방법 이 비교적 취약 하 다. 주요 원인 은 동기 화 전략의 실현 이 여러 소스 파일 로 분리 되 었 기 때문이다. 바 텀 클래스 가 동기 화 전략 을 바 꾸 었 다 고 가정 하고 서로 다른 잠 금 을 바 꾸 어 상 태 를 보호 하면 하위 클래스 가 파 괴 될 것 이다.
세 번 째 방법 은 보조 류 를 사용 하여 client 잠 금 체 제 를 실현 합 니 다.
예 를 들 어 Collections. synchronized List 가 봉 인 된 Array List 는 앞의 두 가지 방법 이 모두 통 하지 않 습 니 다. 클 라 이언 트 코드 는 동기 화 패키지 공장 방법 에서 돌아 오 는 List 대상 의 유형 을 모 르 기 때 문 입 니 다. 이 때 client 잠 금 방식 으로 확장 코드 를 '보조 클래스' 에 넣 습 니 다.
그래서 우 리 는 아주 자 연 스 럽 게 ListHelper 보조 류 를 썼 다.
public class ListHelper<E>{
	public List<E> list = Collections.synchronizedList(new ArrayList<E>());
	public synchronized boolean putIfAbsent(E x){
		boolean absent = !list.contains(x);
		if(absent)
			list.add(x);
		return absent;
	}
}

 어떻게 보면 문제 가 없 지만, 매우 유감스럽게도 이런 방식 은 잘못된 것 이다.
putIfAbsent 는 synchronized 라 고 밝 혔 지만 ListHelper 에 자 물 쇠 를 채 웠 습 니 다. List 는 자신 이나 내부 대상 의 자 물 쇠 를 사용 합 니 다. ListHelper 는 동기 화 된 가상 을 가 져 왔 을 뿐 입 니 다.
Vector 와 동기 화 패키지 클래스 의 문서 에서 Vector 나 패키지 용기 내부 자 물 쇠 를 통 해 client 자 물 쇠 를 지원 합 니 다. 다음은 정확 한 client 자 물 쇠 를 제공 합 니 다.
public class ListHelper<E>{
	public List<E> list = Collections.synchronizedList(new ArrayList<E>());
	public boolean putIfAbsent(E x){
		synchronized (list){
			boolean absent = !list.contains(x);
			if(absent)
				list.add(x);
		return absent;}
	}
}

 하나의 원자 조작 을 통 해 클래스 를 확장 하 는 것 은 취약 합 니 다. 클래스 의 잠 금 코드 를 여러 클래스 에 분포 하기 때 문 입 니 다. 그러나 클 라 이언 트 의 잠 금 코드 는 전혀 무관 한 다른 클래스 에 넣 기 때문에 더욱 취약 합 니 다.
네 번 째 방법 은 조합 (Composition) 방식 을 사용한다.
public  class ImprovedList<T> implements List<T> {
	public final List<T> list;

	public ImprovedList(List<T> list) {
		this.list = list;
	}

	public synchronized boolean putIfAbsent(T x) {
		boolean absent = !list.contains(x);
		if (absent)
			list.add(x);
		return absent;
	}
	
	//...         List     
}

        Improved List 는 내 장 된 자 물 쇠 를 통 해 추가 자 물 쇠 를 추가 합 니 다. 바 텀 List 가 스 레 드 가 안전 한 지 에 관심 이 없습니다. List 가 스 레 드 가 안전 하지 않 거나 족쇄 방식 을 바 꾸 었 더 라 도 Improved 는 일치 하 는 잠 금 체 제 를 제공 하여 스 레 드 안전성 을 실현 합 니 다. 추가 동기 화 층 은 가 벼 운 성능 손실 을 초래 할 수 있 지만 시 뮬 레이 션 과 는 또 하나 가 있 습 니 다.대상 의 잠 금 정책 보 다 는 Improved List 가 더 건장 합 니 다. 사실 우 리 는 자바 모니터 모드 를 사용 하여 기 존 List 를 패키지 하고 클래스 에서 바 텀 List 를 가리 키 는 의외 의 인용 을 하지 않 으 면 스 레 드 안전성 을 확보 할 수 있 습 니 다.

좋은 웹페이지 즐겨찾기