실패 원자적
작업 도중 예외가 발생해도 그 객체는 여전히 정상적으로 사용할 수 있는 상태라면 코드가 더 유용해진다. 검사 예외를 던진 경우라면 호출자가 오류 상태를 복구할 수 있을테니 특히 더 유용할 것이다. 호출된 메서드가 실패하더라도, 해당 객체는 메서드 호출 전 상태를 유지해야한다. 이러한 특성을 '실패 원자적(failure-atomic)' 이라고 한다.
실패 원자적으로 만드는 방법
메서드를 실패 원자적으로 만드는 방법은 다양하다.
1) 불변 객체로 설계한다.
불변 객체는 태생적으로 실패 원자적이다. 메서드가 실패하면 새로운 객체가 만들어지지는 않을 수 있으나, 기존 객체가 불안정한 상태에 빠지는 일은 결코 없다. 불변 객체의 상태는 생성 시점에 고정되어 절대 변하지 않기 때문이다.
2) 가변 객체일 경우, 작업 수행에 앞서 매개변수의 유효성을 검사한다.
객체의 내부 상태를 변경하기 전에 잠재적 예외 가능성 대부분을 걸러낼 수 있다.
Stack 구현 코드 중에서
...
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
Object result = elements[--size];
elements[size] = null; // 다 쓴 참조 해제
return result;
}
...
이 메서드는 처음의 if문에서 size 값을 확인하여 0이면 예외를 던진다. 이 부분을 제거하더라도 스택이 비어있다면 예외가 던져지는데, size의 값이 음수가 되어 다음번 호출도 실패하게 만들며 이때 ArrayIndexOutOfBoundsException 이 발생하게된다. ArrayIndexOutOfBoundsException 은 상황에 어울리지 않는다.
3) 실패할 가능성이 있는 모든 코드를, 객체의 상태를 바꾸는 코드보다 앞에 배치한다.
TreeMap.java
...
public final V put(K key, V value) {
if (!inRange(key))
throw new IllegalArgumentException("key out of range");
return m.put(key, value);
}
...
TreeMap은 원소들을 어떤 기준으로 정렬하는데, 원소를 추가하기 전에 해당 key가 원소를 정렬할 수 있는 기준에 해당하는지 체크한다.
4) 객체의 임시 복사본에서 작업을 수행한 다음, 작업이 성공적으로 완료되면 원래 객체와 교체한다.
데이터를 임시 자료구조에 저장해 작업하는게 더 빠를때 적용하기 좋은 방식이다. 예를들어 어떤 정렬 메서드에서는 정렬을 수행하기 전에 입력 리스트의 원소들을 배열로 옮겨담는다. 정렬에 실패하더라도 입력 리스트에는 변함이 없다.
5) 작업 도중 발생하는 실패를 가로채는 복구 코드를 작성하여 작업 전 상태로 되돌린다.
주로 (디스크 기반의) 내구성을 보장해야하는 자료구조에 쓰이는데 자주 쓰이는 방법은 아니다.
정리
실패 원자성은 일반적으로 권장되는 덕목이지만 항상 달성할 수 있는 것은 아니다. 예를 들어, 두 스레드가 동기화 없이 같은 객체를 동시에 수정한다면 그 객체의 일관성이 깨질 수 있다.
Error는 복구할 수 없으므로 AssertionError에 대해서는 실패 원자적으로 만들려는 시도조차 할 필요가 없다.
실패 원자적으로 만들 수 있더라도 항상 그리 해야하는 것도 아니다. 실패 원자성을 달성하기 위한 비용이나 복잡도가 아주 큰 연산도 있기 때문이다. 그래도 무엇인지 알고나면 실패 원자성을 꽁짜로 얻을 수 있는 경우가 더 많다.
메서드 명세에 기술한 예외라면, 설혹 예외가 발생하더라도 객체의 상태를 메서드 호출 전과 똑같이 유지되어야한다. 이 규칙을 지키지 못한다면 실패 시의 객체 상태를 API 설명에 명시해야한다.
'Book > Effective Java' 카테고리의 다른 글
[교재 EffectiveJava] 아이템 78. 공유 중인 가변 데이터는 동기화해 사용하라 (0) | 2021.12.22 |
---|---|
[교재 EffectiveJava] 아이템 77. 예외를 무시하지 말라 (0) | 2021.12.21 |
[교재 EffectiveJava] 아이템 75. 예외의 상세 메시지에 실패 관련 정보를 담으라 (0) | 2021.12.19 |
[교재 EffectiveJava] 아이템 74. 메서드가 던지는 모든 예외를 문서화하라 (0) | 2021.12.17 |
[교재 EffectiveJava] 아이템 73. 추상화 수준에 맞는 예외를 던져라 (0) | 2021.12.15 |