목록이펙티브자바 (88)
도당탕탕
동시성 컬렉션은 List, Queue, Map과 같은 표준 컬렉션 인터페이스에 동시성을 고려하여 구현한 고성능 컬렉션이다. 높은 동시성에 도달하기 위해 동기화를 각자의 내부에서 수행하는데 컬렉션에서 동시성을 무력화하는 건 불가능하며, 외부에서 락을 추가로 사용하면 오히려 속도가 느려진다. 따라서 동시성 컬렉션의 동시성을 무력화하지 못하기 때문에 여러 메서드를 원자적으로 묶어 호출하는 것도 못한다. 그래서 여러 동작을 하나의 원자적 동작으로 묶는 상태 의존적 메서드가 추가되었다. 예를 들면 다음과 같다. putIfAbsent을 사용한 intern 메서드 private static final ConcurrentMap map = new ConcurrentHashMap(); public static String..
Executor Framework java.util.concurrent 패키지에 존재 기존의 Thread 클래스보다 더 편리한 사용성 제공 객체생성 -> ExecutorService exec = Executors.newSingleThreadExecutor(); 실행 -> exec.execute(runnable); 종료 -> exec.shutdown(); Thread pool 작업의 모든 면을 설정하고 싶다면, Executors.ThreadPoolExecutor 사용 작은 프로그램(local에서 동작하는 프로그램)에 대해서는 Executors.newCachedThreadPool 사용 큰 프로그램(production용)에 대해서는 Executors.newFixedThreadPool 사용 작업의 단위를 tas..
동기화를 과도하게 사용할 경우 성능을 떨어뜨리고, 교착상태에 빠뜨리고, 심지어 예측할 수 없는 동작을 낳기도 한다. 즉 응답 불가와 안전 실패를 피하려면 동기화 메서드나 동기화 블록 안에서는 제어를 절대로 클라이언트에 양도하면 안 된다. 예를 들면 다음과 같다. 동기화된 영역 안에서는 재정의할 수 있는 메소드는 호출하면 안 된다. 클라이언트가 넘겨준 함수 객체를 호출해서도 안된다. 위와 같은 것들을 동기화된 영역에서는 바깥세상에서 온 외계인 메서드라 부른다. 외계인 메서드가 하는 일에 따라 동기화된 영역은 예외를 일으키거나, 교착상태에 빠지거나, 데이터를 훼손할 수도 있다. 구체적인 예를 한번 보자. 잘못된 코드, 동기화 블록 안에서 외계인 메서드를 호출한 경우 public class Observable..
너무 뻔하지만 프로그래머가 잘 어기는 것들 중 하나가 예외를 무시한다는 것이다. API 설계자가 메소드 선언에 예외를 명시하는 까닭은, 그 메소드를 사용할 때 적절한 조치를 취해달라고 말하는 것이다. 예외를 무시하기란 아주 쉽다. try문을 감싸고 catch 블럭에 아무것도 안하면 된다. 하지만 이는 다음과 같은 행위를 무시하는 것이다. 예외는 문제 상황에 잘 대처하기 위해 존재하는데 catch블록을 비워두면 예외가 존재할 이유가 없어진다. 물론 예외를 무시해야 할 때도 있다. 예를 들어 FileInputStream을 닫을 때가 그렇다. 파일의 상태를 변경하지 않았으니 복구할 것이 없으며, 필요한 정보는 이미 다 읽었다는 뜻이니 남은 작업을 중단할 이유도 없을 때 발생한 예외가 그렇다. 따라서 이러한 에..
실패원자성 실행도중 실패한 함수가 실패하더라도, 원본 객체는 변함없는 성질 실패원자성 보장 방법 immutable 객체 생성 함수 실행 전 파라미터를 체크 public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } 실패할만한 로직을 함수의 앞부분에 배치 원본객체를 임시 객체로 복사한뒤, 작업이 성공하면 원본객체를 대체 함수 실행도중 발생한 실패를 캐치하여 원래 상태로 롤백 단, 멀티스레딩 환경에서 적절한 동기화 없이 객체를 변경한 경우, 불가능 정리 실..
자바에서 stack trace는 예외 객체의 toString 메서드를 호출해 출력한다. 보통 예외의 클래스 이름 뒤에 상세 메시지가 붙는 형태로 나타낸다. 이 정보를 통해 프로그래머 혹은 사이트 신뢰성 엔지니어가 실패 원인을 찾고 분석해 해결한다. 따라서 예외의 toString 메소드에 실패 원인에 관한 정보를 가능한 한 많이 담아 반환하는 일은 아주 중요하다. 그렇기 때문에 실패 순간을 포착하려면 발생한 예외에 관여된 모든 매개변수와 필드의 값을 실패 메시지에 담아야 한다. 예를 들면 IndexOutOfBoundException의 상세 메시지는 범위의 최솟값, 최댓값, 그리고 그 범위를 벗어났다는 인덱스의 값을 담아야 한다. 이 정보는 다음과 같이 실패에 관한 많은 것을 알려준다. 인덱스가 최솟값보다 ..
checked exception 뿐 아니라, unchecked exception 또한 문서화해야 한다. unchecked exception에 대해서는, 정상적으로 동작하기 위한 precondition(사전 조건)을 명시해주어야 한다. @throw 어노테이션을 사용해서 각각의 exception을 문서화 하되, runtime exception에 대해서는 throws 키워드를 사용하지 마라. 즉 아래와 같이 사용해서는 안 된다. 보통 @throw tag로 생성된 문서에서 throws 키워드가 있는 경우에는 checked exception으로 인지되고, throws 키워드가 없는 경우에는 unchecked exception으로 인식 되기 때문이다. public void test() throws RuntimeEx..
메서드가 저수준 예외를 처리하지 않고 바깥으로 전파해 고수준에서 예외를 처리하는 일이 간혹 있을 것이다. 사실 이는 내부 구현 방식을 드러나기 때문에 윗 레벨 API를 오염시킨다. 다음 릴리스에서 구현 방식을 바꾸면 다른 예외가 튀어나와 기존 클라이언트 프로그램을 깨지게 할 수도 있다. 이 문제를 피하려면 상위계층에서는 저수준 예외를 잡아 자신의 추상화 수준에 맞는 예외로 바꿔 던져야 한다. 이를 예외 번역이라 한다. 예외 번역 try { ... // 저수준 추상화를 이용한다. } catch (LowerLevelException e) { // 추상화 주순에 맞게 번역한다. throw new HigherLevelException(...); } 다음은 AbstractSequentialList에서 get 메소..