Item 76. Failure Atomic

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

실패 원자적(failure-atomic)이란 호출된 메서드가 실패하더라도 해당 객체는 메서드 호출 전 상태를 유지하는 것을 말한다. 메서드를 실패 원자적으로 만드는 가장 간단한 방법은 불변 객체로 설계하는 것이며, 불변 객체는 실패 원자적이기 때문에 별다른 노력없이 실패 원자적으로 만들 수 있다.

가변 객체 메서드의 실패 원자성 보장 방법

1. 매개변수 유효성 검사

만약 가변 객체의 메서드를 실패 원자적으로 만드는 가장 일반적인 방법은 작업 수행에 앞서 매개변수의 유효성을 검사하여 예외의 가능성을 줄이는 것이다.


public Object pop() {
    if (size == 0) {
        throw new EmptyStackException();
    }
    Object result = elements[--size];
    elements[size] = null;
    return result;
}

size == 0 검사 예외를 던지지 않더라도 elements[--size]에서 ArrayIndexOutOfBoundsException 예외가 발생하게 된다. 하지만 해당 라인까지 가게 되면 size 값이 이미 감소되기 때문에 객체의 상태가 변경되어 실패 원자적이지 않게 된다.

2. 실패할 가능성이 있는 모든 코드를, 객체의 상태를 바꾸는 코드보다 앞에 배치

실제 연산을 수행하기 전에 인수의 유효성 검사 할 수 없을 때 사용하는 방법으로, TreeMap은 어떤 기준으로 정렬하게 되는데, 다른 타입의 원소를 추가하려 하면 ClassCastException 예외를 발생시키게 된다.

3. 임시 복사본 사용

임시 복사본을 만들어 작업을 수행하고, 작업이 성공적으로 완료되면 원래 객체와 교체하는 방법이다. 임시 저장소에 저장하여 작업하는 게 빠를 때 적용하기 좋으며, 작업이 실패하더라도 원래 객체는 변하지 않기 때문에 실패 원자적으로 만들 수 있다.

4. 복구 코드 작성

작업 도중 발생하는 실패를 가로채는 복구 코드를 작성하여 작업 전 상태로 되돌리는 방법이다. 주로 내구성(durability)을 보장해야 하는 자료구조에 적용하기 좋으며, 복구 코드를 작성하는 것이 어렵고, 자주 쓰이는 방법은 아니다.

실패 원자성을 달성하기 어려운 경우

실패 원자성은 일반적으로 권장되지만, 항상 수행해야 하는 것은 아니며 아래와 같은 경우에는 달성하기 어렵거나 하지 않는 것이 좋을 수 있다.

  • 두 스레드가 동기화 없이 같은 객체를 동시에 수정할 때

  • 실패 원자성을 달성하기 위한 비용이나 복잡도가 너무 큰 경우

실패 원자성은 항상 지켜야 하는 것은 아니지만 만약 지키지 못 했을 경우엔 API 설명에 명시해야 한다.

Last updated

Was this helpful?