규칙 64 - 실패 원자성 달성을 위해 노력하라.

JAVA/Effective Java|2018. 6. 5. 23:22

예외가 발생한 다음에도 기존에 사용하던 객체의 상태가 그대로 유지되는 것이 좋다.

쉽게 이야기하면, 메서드 호출이 정상적으로 처리되지 못한 객체의 상태는 메서드 호출 전 상태와 동일해야한다. 이런 속성을 만족하는 메서드는 실패 원자성(failure atomicity)을 갖추었다고 한다.


이런 실패원자성을 해소하기 위한 방법을 알아보자.


먼저 간단하게 변경 불가능 객체로 설계하는 것이다.

-> 왜냐하면 원자성이 있는 객체는 생성된 이후에는 변경되지 않기 때문에 오류가 발생한다고 해도 원자성이 깨지지 않는다.


그럼 변경 가능한 객체의 경우 어떻게 해야할까?

-> 이는 실제 연산을 수행하기전에 인자 유효성을 미리 검사하는 방법이다. (객체가 변경되기 전에 예외를 발생시켜 발생하는 것을 막는것이다.)


아래 예를 살펴보자. 아래 예의 경우 실제 index 또는 elementCount를 사용하기 전에 먼저 예외를 설정함으로써 객체가 변하는것을 막을 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public synchronized void removeElementAt(int index) {
    modCount++;
    if (index >= elementCount) {
      throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                     elementCount);
    }
    else if (index < 0) {
      throw new ArrayIndexOutOfBoundsException(index);
    }
    int j = elementCount - index - 1;
    if (j > 0) {
      System.arraycopy(elementData, index + 1, elementData, index, j);
    }
      elementCount--;
      elementData[elementCount] = null/* to let gc do its work */
}


cs


마지막 접근법은 연산 수행 도중에 발생하는 오류를 가로채는 복구 코드(recovery code)를 작성하는 것이다.


이런 접근법을 통해서 오류가 발생했을 경우에 변경된 객체를 연산이 시작되기 전으로 복구하는 과정을 거쳐야 한다.

그리고 기타 방법으로 임시로 복사를 진행하여 나중에 상태를 바꾸는 방법이 있다.


결론 

모든 방법을 통해 원자성을 유지할 수는 없다. 예를들면, 여러 스레드가 적절한 동기화 없이 변경할 경우 문제가 발생하는 경우에 일관성이 깨지기 때문이다.

그러므로 예외가 아닌 단순 error인 경우에는 실패원자성을 복구하려고 애쓸필요는 없다. 그러므로 비용과 복잡성등을 따져서 원자성을 지키는 것이 맞는지 확인해보고 진행하는 것이 좋다.


만약 이전상태로 돌릴수 없는 경우에는 API문서에 문제 발생에 관한 내용을 자세하게 기재해주어야 한다. 


출처 : 조슈아 블로크, 『 Effective Java 2/E』, 이병준 옮김, 인사이트(2014.9.1), 규칙64

댓글()