Item 64. 실패 원자성 달성을 위해 노력하라.
실패 원자성
메소드의 호출이 정상적으로 처리되지 못한 객체의 상태는, 함수 호출 전 상태와 동일해야 한다. 이 속성을 만족하는 함수는 실패 원자성(failure atomicity) 을 갖추었다고 한다.
실패 원자성을 달성하는 방법 몇가지
- 불변 객체 : 변경 불가능한 객체의 경우, 함수 호출이 실패하면 새로운 객체가 만들어지지 못할 수는 있지만 기존 객체의 일관성이 깨지지 않는다.
-
선행 유효성 검증(pre check validation) : 변경 가능한 객체의 경우, 실제 연산을 수행하기 전에 유효성(validity)을 검사하여 객체를 변경하는 도중에 예외가 발생하는 것을 막아 준다.
public Object pop(){ if(size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; // 만기(obsolete) 참조 제거 return result; }
위 코드 내용 중
if(size == 0) throw new EmptyStackException();
가 선행 유효성 검증(pre check validation) 부분이다. 이 검증 코드가 없으면,size
필드의 일관성이 깨져서 음수로 바뀌게 되어 이 함수를 다시 호출하면 계쇡 문제가 발생한다.또는, 객체 상태를 바꾸기 전에, 실패 할 가능성이 있는 코드를 전부 앞쪽에 배치한다.(계산을 실제로 수행해 보기 전에 인자를 검사할 수 없을 때 이용)
- rollback : 오류가 발생하면 복구코드가 연산 전 상태로 객체를 되돌린다.
- 객체의 임시 복사본(temporary copy) : 객체의 임시 복사본상에서 필요한 연산을 수행하고, 연산이 끝난 다음에 임시 복사본의 내용으로 객체 상태를 바꿈
예를 들어
Collections.sort
는 원소드를 참조하는 비용을 줄이기 위해, 인자로 주어진 리스트를 정렬 전에 임시 배열에 복사한다. 그 덕에 저렬이 실패해도 원래 리스트에는 아무런 손상이 없다.
실패 원자성을 달성할 수 없는 경우
- 멀티쓰레드를 통해 안전하지 않은 상태 객체 상태 제어 시 : 애초에 객체 일관성이 깨지고 복구를 어떤 쓰레드를 기준으로 해야할지 모호해짐
- 복구 비용이 심각하게 커지는 경우
명심할 것
- 예외와는 달리 오류(error)는 복구가 불가능하며, 오류를 던지는 경우에는 실패 원자성을 보존하려 애쓸 필요가 없다는 것이다.
- 함수 명세에 포함된 예외가 발생하더라도 객체 상태는 함수 호출 이전과 동일하게 유지되어야 한다.