실패 원자적(failure-atomic): 호출된 메서드가 실패하더라도 해당 객체는 메서드 호출 전 상태를 유지해야 한다.
예를 들어 작업 도중 예외가 발생하도 그 객체는 여전히 정상적으로 사용할 수 있는 상태.
메서드를 실패 원자적으로 만드는 방법은 다양하다.
불변 객체로 설계하는 것. 메서드가 실패하면 새로운 객체가 만들어지지 않을 수는 있으나 기존 객체가 불안정한 상태에 빠지는 일은 없다. 불변 객체의 상태는 생성 시점에 고정되어 절대 변하지 않기 때문이다.
작업 수행에 앞서 매개변수의 유효성 검사를 해야 한다. 객체의 내부 상태를 변경하기 전에 잠재적 예외의 가능성 대부분을 걸러낼 수 있다.
public Object pop() {
if (size == 0 )
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null;
return result;
}
궁금증
예전 다른 아이템에서는 생성자에서 매개변수 검사를 우선 필드에 할당하고 검사를 하라고 했다. (validate 된 이후 찰나에 다른 스레드에서 값을 바꿀 수도 있으니). 상충되는 얘기가 아닌가?
원래의 데이터에는 문제가 발생하지 않는다.
작업 도충 발생하는 실패를 가로채는 복구 코드를 작성하여 작업 전 상태로 되돌리는 방법이다. 자주 쓰이는 것은 아니다.
실패 원자성은 항상 달성할 수 있는 건 아니다. 두 스레드가 동기화 없이 같은 객체를 수정하면 그 객체의 일관성이 깨질 수 있다. 따라서 ConcurrentModificationException
은 잡아냈다고 해도 그 객체가 쓸 수 있는 상황이라고 생각해서는 안된다.
메서드 명세에 기술한 예외라면 예외가 발생하더라도 객체의 상태는 메서드 호출 전과 똑같이 유지돼야 하는 것이 기본 규칙이다. 이 규칙을 지키지 못하면 실패 시의 객체 상태를 API 설명에 명시해야 한다.