본문 바로가기

독서찰기(讀書札記)/이펙티브 자바

(73)
[아이템 84] 프로그램의 동작을 스레드 스케줄러에 기대지 말라 [Why] 구체적인 스케줄링 정책은 운영체제마다 다를 수 있다. 정확성이나 성능이 스레드 스케줄러에 따라 달라지는 프로그램이라면 다른 플랫폼에 이식하기 어렵다. [How] 견고하고 빠릿하고 이식성 좋은 프로그램을 작성하는 가장 좋은 방법은 실행 가능한 스레드의 평균적인 수를 프로세서 수보다 지나치게 많아지지 않도록 하는 것이다. 실행 준비가 된 스레드들은 맡은 작업을 완료할 때까지 계속 실행되도록 만들자. 이런 프로그램이라면 스레드 스케줄링 정책이 아주 상이한 시스템에서도 동작이 크게 달라지지 않는다. 여기서 실행 가능한 스레드의 수와 전체 스레드 수는 구분해야 한다. 전체 스레드 수는 훨씬 많을 수 있고, 대기 중인 스레드는 실행 가능하지 않다. 실행 가능한 스레드 수를 적게 유지하는 주요 기법은 각 ..
[아이템 83] 지연 초기화는 신중히 사용하라 [배경] 지연 초기화(lazy initialization): 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법 주로 최적화 용도로 쓰이지만, 클래스와 인스턴스 초기화 때 발생하는 위험한 순환 문제를 해결하는 효과도 있다. [Why] 지연 초기화는 양날의 검이다. 클래스 혹은 인스턴스 생성 시의 초기화 비용은 줄지만 그 대신 지연 초기화하는 필드에 접근하는 비용은 커진다. 지연 초기화하려는 필드들 중 결국 초기화가 이뤄지는 비율에 따라, 실제 초기화에 드는 비용에 따라, 초기화된 각 필드를 얼마나 빈번히 호출하느냐에 따라 지연 초기화가 실제로는 성능을 느려지게 할 수도 있다. 지연 초기화가 필요할 때가 있다. 해당 클래스의 인스턴스 중 그 필드를 사용하는 인스턴스의 비율이 낮은 반면, 그 필드..
[아이템 82] 스레드 안전성 수준을 문서화하라 [Why] API 문서에서 동시성에 대한 언급이 없으면 그 클래스 사용자는 나름의 가정을 해야만 한다. 그 가정이 틀리면 클라이언트 프로그램은 동기화를 충분히 하지 못하거나(아이템78) 지나치게 한(아이템79) 상태일 것이며, 두 경우 모두 심각한 오류로 이어질 수 있다. 자바독이 기본 옵션에서 생성한 API 문서에는 synchronized 한정자가 포함되지 않는다. (구현 이슈일 뿐이기 때문) 스레드 안전성에도 수준이 나뉜다. 멀티스레드 환경에서도 API를 안전하게 사용하게 하려면 클래스가 지원하는 스레드 안전성 수준을 정확히 명시해야 한다. [How] 다음 목록은 스레드 안전성이 높은 순으로 나열한 것이다. 불변(immutable): 이 클래스의 인스턴스는 마치 상수와 같아서 외부 동기화도 필요 없다..
[아이템 74] 메서드가 던지는 모든 예외를 문서화하라 [Why] 공통 상위 클래스 하나로 뭉뚱그려 선언하면, 메서드 사용자에게 각 예외에 대처할 수 있는 힌트를 주지 못할뿐더러, 같은 맥락에서 발생할 여지가 있는 다른 예외들까지 삼켜버릴 수 있어 API 사용성을 크게 떨어뜨린다. 자신이 일으킬 수 있는 오류들이 무엇인지 알려주면 프로그래머는 자연스럽게 해당 오류가 나지 않도록 코딩한다. 잘 정비된 비검사 예외 문서는 사실상 그 메서드를 성공적으로 수행하기 위한 전제조건이 된다. 발생 가능한 비검사 예외를 문서로 남기는 일은 인터페이스 메서드에서 특히 중요하다. 이 조건이 인터페이스의 일반 규약에 속하게 되어 그 인터페이스를 구현한 모든 구현체가 일관되게 동작하기 때문이다. [How] 검사 예외는 항상 따로 선언하고, 각 예외가 발생하는 상황을 자바독의 @t..
[아이템 73] 추상화 수준에 맞는 예외를 던지라 [Why] 수행하려는 일과 관련 없어 보이는 예외가 튀어나오면 당황스러울 것이다. 메서드가 저수준 예외를 처리하지 않고 바깥으로 전파해버릴 때 종종 일어나는 일이다. 이는 내부 구현 방식을 드러내어 윗 레벨 API를 오염시키기도 한다. [How] 상위 계층에서는 저수준 예외를 잡아 자신의 추상화 수준에 맞는 예외로 바꿔 던져야 한다. 이를 예외 번역(exception translation)이라 한다. try { ... // 저수준 추상화를 이용한다. } catch (LowerLevelException e) { // 추상화 수준에 맞게 번역한다. throw new HigherLevelException(...); } 예외를 번역할 때, 저수준 예외가 디버깅에 도움이 된다면 예외 연쇄(exception cha..
[아이템 72] 표준 예외를 사용하라 [Why] 표준 예외를 재사용하면 얻는 게 많다. 우리의 API가 다른 사람이 익히고 사용하기 쉬워진다. 많은 프로그래머에게 이미 익숙해진 규약을 그대로 따르기 때문이다. 우리의 API를 사용한 프로그램도 낯선 예외를 사용하지 않게 되어 읽기 쉽게 된다는 장점도 크다. 예외 클래스 수가 적을수록 메모리 사용량도 줄고 클래스를 적재하는 시간도 적게 걸린다. [How] Exception, RuntimeException, Throwable, Error는 직접 재사용하지 말자. 이 예외들은 다른 예외들의 상위 클래스이므로, 즉 여러 성격의 예외들을 포괄하는 클래스이므로 안정적으로 테스트할 수 없다. 예외 주요 쓰임 IllegalArgumentException 허용하지 않는 값이 인수로 건네졌을 때(null은 따..
[아이템 71] 필요 없는 검사 예외 사용은 피하라 [배경] 검사 예외를 제대로 활용하면 API와 프로그램의 질을 높일 수 있다. 결과를 코드로 반환하거나 비검사 예외를 던지는 것과 달리, 검사 예외는 문제를 프로그래머가 처리하여 안전성을 높이게끔 해준다. [Why] 검사 예외를 과하게 사용하면 쓰기 불편한 API가 된다. 어떤 메서드가 검사 예외를 던질 수 있다고 선언됐다면, 이를 호출하는 코드에서는 catch 블록을 두어 그 예외를 붙잡아 처리하거나 더 바깥으로 던져 문제를 전파해야만 한다. 더구나 검사 예외를 던지는 메서드는 스트림 안에서 직접 사용할 수 없기 때문에 자바 8부터는 부담이 더욱 커졌다. 검사 예외가 프로그래머에게 지우는 부담은 메서드가 단 하나의 검사 예외만 던질 때가 특히 크다. 이미 다른 검사 예외도 던지는 상황에서 또 다른 검사..
[아이템 70] 복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라 [배경] 자바는 문제를 상황을 알리는 타입(throwable)으로 검사 예외, 런타임 예외, 에러 이렇게 세 가지를 제공한다. 비검사 throwable은 두 가지로, 런타임 예외와 에러다. 둘 다 동작 측면에서는 다르지 않다. 이 둘은 프로그램에서 잡을 필요가 없거나 혹은 통상적으로는 잡지 말아야 한다. 프로그램에서 비검사 예외나 에러를 던졌다는 것은 복구가 불가능하거나 더 실행해봐야 득보다는 실이 많다는 뜻이다. 이런 throwable을 잡지 않은 스레드는 적절한 오류 메시지를 내뱉으면 중단된다. [How] 호출하는 쪽에서 복구하리라 여겨지는 상황이라면 검사 예외를 사용하라. 이것이 검사와 비검사 예외를 구분하는 기본 규칙이다. 검사 예외를 던지면 호출자가 그 예외를 catch로 잡아 처리하거나 더 바..