티스토리 뷰

반응형

실패 원자적(failure-atomic)

예외가 발생해도 여전히 정상적으로 사용할 수 있는 상태라면 얼마나 좋을까?

호출된 메서드가 실패하더라도 해당 객체는 호출 전 상태를 유지해야한다.

 

 

실패 원자적인 메서드 만드는 방법

1) 불변 객체 [아이템 17]로 설계하기

  • 불변 객체는 태생적으로 실패 원자적
  • 불변 객체의 상태는 생성 시점에 고정되어 절대 변하지 않기 때문에 메서드가 실패해도 불안정한 상태에 빠지지 않는다.

 

2) 가변 객체의 메서드의 경우 작업 수행 전 매개변수의 유효성을 검사하기 [아이템 49]

  • 객체의 내부 상태를 변경하기 전에 잠재적 예외 가능성 대부분을 걸러낼 수 있는 방법
public Object pop() {
    if (size == 0)  //매개변수의 유효성 검사
        throw new EmptyStackException();  
    Object result = elements[--size];
    elements[size] = null; // 다 쓴 참조 해제
    return result;
}

위 코드는 size 값을 확인하여 유효성 검사를 하고 0이면 예외를 던지도록 한다.

이 부분이 없어도 스택이 비어있다면 아래서 예외를 던지지만, size값이 음수가 되어 다음번 호출도 실패하게 만든다.

이 때 던지는 ArrayIndexOutOfBoundsException은 추상화 수준이 상황에 어울리지 않다. [아이템 73]

  • 실패할 가능성이 있는 모든 코드를 객체의 상태를 바꾸는 코드보다 앞에 배치하는 방법도 있음.
    • ex) 정렬을 할 수 있는 TreeMap에 원소를 추가하려면 그 원소가 TreeMap의 정렬 기준에 따라 비교할 수 있는 타입이어야한다. 엉뚱한 타입의 원소 추가는 트리 변경에 앞서 들어갈 위치를 찾는 과정에서 ClassCastException을 던질 것이다.

 

3) 객체의 임시 복사본에서 작업을 수행한 다음 작업이 성공적으로 완료되면 원래 객체와 교체하기

데이터를 임시 자료구조에 저장해 작업하는게 더 빠를 때 사용하기 좋은 방법

ex) 어떤 정렬 메서드에서는 정렬을 수행하기 전에 입력 리스트의 원소들을 배열로 옮겨 담는다. (배열이 원소들에 더 빨리 접근할 수 있어서 정렬 알고리즘의 반복문에서 더 성능이 좋기 때문)

 

4) 작업 도중 발생하는 실패를 가로채는 복구 코드를 작성하여 작업 전 상태로 되돌리기

주로 디스크 기반의 내구성을 보장해야하는 자료구조에 쓰이는데 흔한 방법은 아니다.

 

 

실패 원자성을 항상 달성할 수 있을까?

  • 실패 원자성은 권장되지만 항상 달성할 수 있는 것은 아니다.
    • ex) 두 스레드가 동기화 없이 같은 객체를 동시에 수정한다면 일관성이 깨짐
  • Error는 복구할 수 없으므로 AssertionError에 대해서는 실패 원자적으로 만들려는 시도조차 할 필요가 없음
  • 실패 원자적으로 만들 수 있더라도 항상 그렇게 해야하는 것은 아님 → 비용이나 복잡도가 아주 큰 연산도 있어서

 

결론

메서드 명세에 기술한 예외라면 설혹 예외가 발생하더라도 객체의 상태는 메서드 호출 전과 똑같이 유지돼야한다는 것이다.

이 규칙을 지키지 못한다면 실패 시의 객체 상태를 API 설명에 명시해야한다.

반응형
댓글
반응형
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday