-
[Effective Java] Item 76. 가능한 실패 원자적으로 만들라Java/Effective Java 2020. 11. 1. 12:41반응형
이번 주제에서 이야기하는 바는 호출된 메서드가 실패하더라도 해당 객체는 메서드 호출 전 상태를 유지해야 한다는 내용입니다. 이러한 특성을 실패 원자적(failure-atomic)이라고 합니다.
실패 원자적으로 만드는 방법
불변 객체로 설계하는 방법
불변 객체는 태생적으로 실패 원자적입니다. 메서드가 실패하면 새로운 객체가 만들어지지는 않을 수 있으나 기존 객체가 불안정한 상태에 빠지는 일이 없습니다. 불변 객체의 상태는 생성 시점에 고정되어 절대 변하지 않기 때문입니다.
작업 수행에 앞서 매개변수의 유효성을 검사하는 방법
가변 객체의 메서드를 실패 원자적으로 만드는 방법은 작업 수행에 앞서 매개변수의 유효성을 검사하는 것입니다. 객체의 내부 상태를 변경하기 전에 잠재적 예외의 가능성을 대부분 걸러낼 수 있는 방법입니다.
Stack의 pop() 메서드 예시
public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; //다 쓴 참조 해제 return result; }
위의 메서드는 처음의 if 문에서 size의 값을 확인하여 0이면 예외를 던집니다. 이 부분을 제거하더라도 스택이 비었다면 여전히 예외를 던지지만 size 값이 음수가 되어 다음번 호출도 실패하게 만들며, 이때 던지는 ArrayIndexOutOfBoundsException은 추상화 수준이 상황에 어울리지 않습니다.
이와 비슷한 취지로 실패할 가능성이 있는 모든 코드를, 객체의 상태를 바꾸는 코드보다 앞에 배치하는 방법도 있습니다. 계산을 수행해보기 저넹는 인수의 유효성을 검사해볼 수 없을 때 앞서의 방식에 덧붙여 쓸 수 있는 기법입니다.
원소들을 특정 기준에 따라 정렬하는 TreeMap을 예로, TreeMap에 어떤 원소를 추가하려면 그 원소는 TreeMap의 기준에 따라 비교할 수 있는 타입이어야 합니다. 엉뚱한 타입의 원소를 추가하려 들면 트리를 변경하기 앞서, 해당 원소가 들어갈 위치를 찾는 과정에서 ClassCastException을 던지게 됩니다.
객체의 임시 복사본을 통한 방법
이 방법은 객체의 임시 복사본에서 작업을 수행한 다음, 작업이 성공적으로 수행되면 원래의 객체와 교체하는 방법입니다. 데이터의 임시 자료구조에 저장해 작업하는 게 더 빠를 때 적용하기 좋은 방식입니다.
예를 들어, 어떤 정렬 메서드에서는 정렬을 수행하기 전에 입력 리스트의 원소들을 배열로 옮겨 담습니다. 배열을 사용하면 정렬 알고리즘의 반복문에서 원소들에 훨씬 빠르게 접근할 수 있기 때문입니다. 혹시나 정렬에 실패하더라도 입력 리스트는 변하지 않는 효과를 덤으로 얻게 됩니다.
작업 도중에 발생하는 실패를 가로채서 복구하는 방법
이 방법은 작업 도중에 발생하는 실패를 가로채는 복구 코드를 작성하여 작업 전의 상태로 되돌리는 방법입니다. 주로 (디스크 기반의) 내구성(durability)을 보장해야 하는 자료구조에 쓰이는데, 자주 쓰이는 방법은 아닙니다.
실패 원자성은 항상 달성할 수 있는가?
실패 원자성은 항상 권장되지만 항상 달성할 수 있는 것은 아닙니다. 예를 들어 두 쓰레드가 동기화 없이 같은 객체에 접근하여 수정한다면 그 객체의 일관성이 깨질 수 있습니다. 따라서 ConcurrentModificationException을 잡아냈다고 해서 그 객체가 여전히 쓸 수 있는 상태라고 가정해서는 안됩니다.
실패 원자성을 달성하기 위한 비용이나 복잡도가 아주 큰 연산이 있는 경우에는 실패 원자적으로 만들었을 경우의 트레이드 오프를 잘 따져보아야 합니다.
참고자료
반응형'Java > Effective Java' 카테고리의 다른 글
[Effective Java] Item 79. 과도한 동기화는 피하라 (0) 2020.11.06 [Effective Java] Item 78. 공유중인 가변 데이터는 동기화해 사용하라 (0) 2020.11.04 [Effective Java] Item 69. 예외는 진짜 예외 상황에만 사용하라 (0) 2020.10.27 [Effective Java] Item 65. 리플렉션보다는 인터페이스를 사용하라 (0) 2020.10.24 [Effective Java] Item 64. 객체는 인터페이스를 사용해 참조하라 (0) 2020.10.24