76. 가능한 한 실패 원자적으로 만들라

신명철·2022년 5월 1일
0

Effective Java

목록 보기
72/80

실패 원자적(failure-atomic)

작업 도중 예외가 발생해도 그 객체는 여전히 정상적으로 사용할 수 있는 상태라면 멋질 것이다. 검사 예외를 던진 경우라면, 호출자가 오류 상태를 복구할 수 있을 테니 특히 더 유용할 것이다. 일반화해 이야기하면, 호출된 메서드가 실패하더라도 해당 객체는 메서드 호출 전 상태를 유지해야 한다. 이러한 특성을 실패 원자적이라고 한다.

메서드를 실패 원자적으로 만드는 방법

불변 객체

메서드를 실패 원자적으로 만드는 방법은 다양하지만 가장 간단한 방법은 객체를 불변 객체로 만드는 것이다. 불변 객체는 태생적으로 실패 원자적이기 때문이다.

작업 전 유효성 검사

가변 객체의 메서드를 실패 원자적으로 만드는 가장 흔한 방법은 작업 수행에 앞서 매개변수의 유효성을 검사하는 것이다. 객체 내부 상태를 변경하기 전 잠재적 예외 가능성 대부분을 걸러낼 수 있는 방법이다.

public Object pop(){
	if(size == 0) 
    	throw new EmptyStackException();
    ... // 내부 상태 변경
}

객체의 임시 복사본 사용

객체의 임시 복사본에서 작업으 수행한 다음, 작업이 성공적으로 완료되면 원래 객체와 교체하는 방법이 있다. 데이터를 임시 자료구조에 저장하고 작업하는 게 더 빠를 때 좋은 방식이다.

예를 들어서, 어떤 정렬 메서드에서는 정렬을 수행하기 전에 입력 리스트의 원소들을 배열로 옮겨 담는다. 배열을 사용하면 정렬 알고리즘의 반복문에서 원소들에 훨씬 빠르게 접근할 수 있기 때문이다. 물론 이는 성능을 높이고자 하는 결정이지만 혹시나 정렬에 실패하더라도 입력 리스트는 변하지 않는 효과를 덤으로 얻는다.

실패를 가로채는 복구 코드 작성

작업 도중 발생하는 실패를 가로채는 복구 코드를 작성해서 작업 전 상태로 되돌리는 방법이다. 주로 내구성을 보장해야 하는 (디스크 기반의)자료구조에 쓰이는데 자주 쓰이는 방법은 아니다.


실패 원자성은 일반적으로 권장되지만 항상 달성할 수 있는 것은 아니다. 예를 들어서 두 쓰레드가 동기화없이 같은 객체를 동시에 수정한다면 그 객체의 일관성이 깨질 수 있다. 따라서 ConcurrentModificationException을 잡아냈다 해서 그 객체가 여전히 쓸 수 있는 상태라고 가정해서는 안된다.

한편, Error는 복구할 수 없으므로 AssertionError에 대해서는 실패 원자적으로 만들려는 시도조차 할 필요가 없다.

실패 원자적으로 만들수 있다고 해도 항상 그리 해야 하는 것도 아니다. 실패 원자성을 달성하기 위해서 비용이나 복잡도가 아주 큰 연산도 있기 때문이다.

profile
내 머릿속 지우개

0개의 댓글