맞는데 왜 틀릴까..?

Java/Effective Java 77

[Effective Java] Item 83. 지연 초기화는 신중히 사용하라

지연 초기화 (lazy initialization)는 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법이다. 따라서 값이 전혀 쓰이지 않으면 초기화도 결코 일어나지 않는다. 이 기법은 정적 필드와 인스턴스 필드 모두에 사용할 수 있다. 주로 최적화 용도로 쓰이지만, 클래스와 인스턴스 초기화 때 발생하는 위험한 순환 문제를 해결하는 효과도 있다. 지연 초기화 특징 지연 초기화는 양날의 검이다. 다른 최적화처럼 필요할 때까지는 하지 않는 게 좋다. 클래스 혹은 인스턴스 생성 시의 초기화 비용은 줄지만 그 대신 지연 초기화하는 필드에 접근하는 비용은 커진다. 실제 성능이 느려지는 이유 지연초기화하려는 필드들 중 초기화가 이뤄지는 비율 실제 초기화에 드는 비용 초기화된 각 필드를 얼마나 빈번히 호출..

Java/Effective Java 2023.08.21

[Effective Java] Item 82. 스레드 안전성 수준을 문서화하라

한 메서드를 여러 스레드가 동시에 호출할 때 그 메서드가 어떻게 동작하느냐는 해당 클래스와 이를 사용하는 클라이언트 사이의 중요한 계약과 같다. API 문서에 아무런 언급이 없다면 사용자가 나름의 가정을 해야 하고, 그 가정이 틀리다면 심각한 오류로 이어질 수 있다. 자바독 기본 옵션에서 생성한 API 문서에는 synchronized 한정자가 포함되지 않는다. 메서드 선언에 synchronized 한정자를 선언할지는 규현 이슈일 뿐 API에 속하지 않는다. 따라서 API 문서에 synchronized 한정자가 보이더라도 스레드 안전하다고 믿기 어렵다. 스레드 안정성 수준 멀티스레드 환경에서도 API를 안전하게 사용하게 하려면 클래스가 지원하는 스레드 안전성 수준을 정확히 명시해야 한다. 스레드 안전성이 ..

Java/Effective Java 2023.08.21

[Effective Java] Item 81. wait와 notify보다는 동시성 유틸리티를 애용하라

자바 5에서 도입된 고수준의 동시성 유틸리티가 이전의 wait와 notify로 하드코딩해야 했던 전형적인 일들을 대신 처리해 준다. wait와 notify는 올바르게 사용하기 아주 까다로우니 고수준 동시성 유틸리티를 사용하자. java.util.concurrent의 고수준 유틸리티는 세 범주로 나뉜다. 실행자 프레임워크 동시성 컬렉션 (concurrent collection) 동기화 장치 (synchronizer) 동시성 컬렉션 List, Queue, Map 같은 표준 컬렉션 인터페이스에 동시성을 가미해 구현한 고성능 컬렉션이다. 높은 동시성에 도달하기 위해 동기화를 각자의 내부에서 수행한다. 따라서 동시성 컬렉션에서 동시성을 무력화하는 건 불가능하며, 외부에서 락을 추가로 사용하면 오히려 속도가 느려진..

Java/Effective Java 2023.08.20

[Effective Java] Item 80. 스레드보다는 실행자, 태스크, 스트림을 애용하라

실행자 프레임워크 (Executor Framework) java.util.concurrent 패키지는 실행자 프레임워크라고 하는 인터페이스 기반의 유연한 태스크 실행 기능을 담고 있다. //작업 큐 생성 ExecutorService exec = Executors.newSingleThreadExecutor(); //실행자에 task(작업) 넘기기 exec.execute(runnalbe); //실행자 종료 exec.shutdown(); 단 한 줄로 작업 큐를 생성하고, task를 넘기며, 실행자를 우아하게 종료시킬 수 있다. 실행자 서비스의 주요 기능 특정 task가 완료되기를 기다린다. task 모음 중 아무것 하나(invokeAny 메서드) 혹은 모든 task(invokeAll 메서드)가 완료되기를 기다..

Java/Effective Java 2023.08.20

[Effective Java] Item 79. 과도한 동기화는 피하라

과도한 동기화는 성능을 떨어뜨리고, 교착상태에 빠뜨리고, 예측할 수 없는 동작을 낳기도 한다. 응답 불가와 안전 실패를 피하려면 동기화 메서드나 동기화 블록 안에서는 제어를 절대로 클라이언트에 양도하면 안 된다. 동기화된 영역 안에서는 재정의할 수 있는 메서드는 호출하면 안 되며, 클라이언트가 넘겨준 함수 객체를 호출해서도 안된다. 외계인 메서드 (Alien method) 외계인 메서드 (alien method) : 동기화된 영역을 포함한 클래스 관점에서 클라이언트가 넘겨준 함수 객체 또는 재정의할 수 있는 메서드 외계인 메서드가 하는 일에 따라 동기화된 영역은 예외를 일으키거나, 교착상태에 빠지거나, 데이터를 훼손할 수도 있다. 잘못된 코드 예시 - 동기화 블록 안에서 외계인 메서드 호출 클래스의 클라..

Java/Effective Java 2023.08.20

[Effective Java] Item 78. 공유 중인 가변 데이터는 동기화해 사용하라

synchronized는 해당 메서드나 블록을 한 번에 한 스레드씩 수행하도록 보장한다. 동기화의 두가지 용도 배타적 실행 한 스레드가 변경하는 중이라서 상태가 일관되지 않은 순간의 객체를 다른 스레드가 보지 못하게 막는다. 스레드 사이의 안정적 통신 동기화 없이는 한 스레드가 만든 변화를 다른 스레드에서 확인하지 못할 수 있다. 동기화는 일관성이 깨진 상태를 볼 수 없게 하는 것은 물론, 동기화된 메서드나 블록에 들어간 스레드가 같은 lock의 보호하에 수행된 모든 이전 수정의 최종 결과를 보게 해 준다. 동기화는 배타적 실행뿐 아니라 스레드 사이의 안정적인 통신에 꼭 필요하다. long, double 외의 변수를 일고 쓰는 동작은 원자적(atomic)이다 여러 스레드가 같은 변수를 동기화 없이 수정하..

Java/Effective Java 2023.08.13

[Effective Java] Item 77. 예외를 무시하지 말라

API 설계자가 메서드 선언에 예외를 명시하는 까닭은 그 메서드를 사용할 때 적절한 조치를 취해달라고 말하는 것이다. 해당 메서드 호출을 try문으로 감싼 후 catch 블록에서 아무 일도 하지 않으면 예외는 아주 쉽게 무시된다. catch 블록을 비워두면 예외가 존재할 이유가 없어진다. 예외를 무시해야 할 때 FileInputStream을 닫을 때 입력 전용 스트림이므로 파일의 상태를 변경하지 않았으니 복구할 것이 없으며, 스트림을 닫는다는 건 필요한 정보는 이미 다 읽었다는 뜻이니 남은 작업을 중단할 이유도 없다. 예외를 무시하기로 했다면 catch 블록 안에 그렇게 결정하기로한 이유를 주석으로 남기고 예외 변수의 이름도 ignored로 바꾸자. public class IgnoredExceptionE..

Java/Effective Java 2023.08.13

[Effective Java] Item 76. 가능한 한 실패 원자적으로 만들라

실패 원자적 (failure-atomic) : 호출된 메서드가 실패하더라도 해당 객체는 메서드 호출 전 상태를 유지해야 한다. 메서드를 실패 원자적으로 만드는 방법 1. 불변 객체로 설계 불변 객체는 태생적으로 실패 원자적이다. 메서드가 실패하면 새로운 객체가 만들어지지는 않을 수 있으나 기존 객체가 불안정한 상태에 빠지는 일은 없다. 2. 작업 수행에 앞서 매개변수의 유효성 검사 가변 객체의 메서드를 실패 원자적으로 만드는 가장 흔한 방법 객체의 내부 상태를 변경하기 전에 잠재적 예외의 가능성 대부분을 걸러낼 수 있다. 계산을 수행해 보기 전에는 인수의 유효성을 검사해 볼 수 없다면, 실패할 가능성이 있는 모든 코드를 객체의 상태를 바꾸는 코드보다 앞에 배치 ex) TreeMap TreeMap은 내부에..

Java/Effective Java 2023.08.12

[Effective Java] Item 75. 예외의 상세 메시지에 실패 관련 정보를 담으라

예외를 잡지 못해 프로그램이 실패하면 자바 시스템은 그 예외의 스택 추적 정보를 자동으로 출력한다. 스택 추적은 예외 객체의 toString 메서드를 호출해 얻는 문자열로, 보통 예외 클래스 이름 뒤에 상세 메시지가 붙는 형태다. 예외 상세 메시지 예외의 toString 메서드에 실패 원인에 관한 정보를 가능한 한 많이 담아 반환하는 일은 중요하다. 실패 순간을 포착하려면 발생한 예외에 관여된 모든 매개변수와 필드의 값을 실패 메시지에 담아야 한다. ex)IndexOutOfBoundsException의 상세 메시지는 범위의 최솟값, 최댓값, 그 범위를 벗어난 인덱스의 값을 모두 담는다. 보안과 관련한 정보는 주의해서 다뤄야 한다. 스택 추적 정보는 많은 사람이 볼 수 있으므로 상세 메시지에 비밀번호나 암..

Java/Effective Java 2023.08.12

[Effective Java] Item 74. 메서드가 던지는 모든 예외를 문서화하라

메서드가 던지는 예외는 그 메서드를 올바로 사용하는 데 아주 중요한 정보다. 따라서 각 메서드가 던지는 예외 하나하나를 문서화하는 데 충분한 시간을 쏟아야 한다. 자바독 @throws 태그 검사 예외는 항상 따로따로 선언하고, 각 예외가 발생하는 상황을 자바독의 @throws 태그를 사용하여 정확히 문서화하자. 공통 상위 클래스 하나로 뭉뚱그려 선언하지는 말자. 극단적인 예로 메서드가 Exception이나 Throwable을 던진다고 선언해서는 안된다. 메서드 사용자에게 각 예외에 대처할 수 있는 힌트를 주지 못할뿐더러, 같은 맥락에서 발생할 여지가 있는 다른 예외들까지 삼켜버릴 수 있어 API 사용성을 크게 떨어뜨린다. JWT Access 토큰을 재발급해주는 메서드 main은 오직 JVM만이 호출하므로..

Java/Effective Java 2023.08.11