JAVA 104

[아이템 84] 프로그램의 동작을 쓰레드 스케줄러에 기대지 말라

1. 서론 여러 쓰레드가 실행 중일 경우 OS의 쓰레드 스케줄러가 어떤 쓰레드를 얼마나 오래 실행할지 결정 OS마다 구체적인 스케줄링 정책은 다를 수 있기 때문에 특정 정책에 의존해서는 안됨 정확성이나 성능이 쓰레드 스케줄러에 종속적인 프로그램이라면 타 플랫폼에 이식하기 어려움 2. 좋은 프로그램을 작성하기 위한 원칙 다음 원칙을 지키면 쓰레드 스케줄링 정책이 변경되어도 크게 영향받지 않습니다. 프로세서 수보다 실행 가능한 쓰레드의 평균 수가 지나치게 많아지지 않도록 설정 실행 준비가 된 쓰레드들은 맡은 작업을 완료할 때까지 계속 실행되어야 함 3. 실행 가능한 쓰레드 수를 적게 유지하기 위한 원칙 각 쓰레드가 작업을 완료한 뒤 다음 작업이 생길 때까지 대기하도록 하는 것이 중요 이는 ThreadPool..

JAVA/Effective Java 2024.04.22

[Java] 자바 동시성 프레임워크

ThreadPool 다수의 쓰레드를 미리 생성하고 관리하여 작업을 효율적으로 처리하는 디자인 패턴 자바에서는 쓰레드 풀을 사용할 수 있도록 Executor 프레임워크 제공 1. ThreadPool이 필요한 이유 쓰레드 생성 비용 절감 쓰레드 재사용 동시성 제어 대량 요청으로부터 시스템 보호 1.1 쓰레드 생성 비용 절감 쓰레드 생성은 비싼 작업 따라서 쓰레드 풀은 쓰레드를 미리 생성하고 초기화하여 대기 상태로 유지함으로써 쓰레드 생성 비용을 절감시킴 1.2 쓰레드 재사용 쓰레드 풀은 작업이 종료된 쓰레드를 대기 상태로 전환시킨 뒤 재사용하며 반복적인 쓰레드 생성 및 삭제에 따른 오버헤드를 피하고 성능 향상 도모 1.3 동시성 제어 쓰레드 풀은 동시에 실행되는 쓰레드의 개수를 제어함에 따라 시스템 리소스의..

[아이템 83] 지연 초기화는 신중히 사용하라

지연 초기화 필드의 초기화 시점을 해당 값이 처음 필요할 때까지 늦추는 기법 값이 전혀 쓰이지 않을 경우 초기화가 일어나지 않음 정적 필드와 인스턴스 필드 모두 적용 가능 주로 최적화 용도로 쓰이지만, 클래스와 인스턴스 초기화 시 발생하는 `위험한 순환` 문제를 해결하는 효과도 있음 위험한 순환(Perilous Cycle) 문제는 초기화하는 동안 객체가 자기 자신을 참조하는 경우에 발생할 수 있는 문제 ex) 클래스나 객체의 생성자에서 다른 객체를 생성하고, 이 생성된 객체가 다시 자신을 참조하는 경우 위험한 순환 문제가 발생 (무한 루프) 1. 지연 초기화는 필요할 때까지는 하지 말자 지연 초기화는 양날의 검 클래스 혹은 인스턴스 생성 시 발생하는 초기화 비용은 줄지만 지연 초기화하는 필드에 접근하는 ..

JAVA/Effective Java 2024.04.11

[아이템 82] 쓰레드 안전성 수준을 문서화하라

한 메서드를 여러 쓰레드가 동시에 호출할 때, 해당 메서드가 어떻게 동작하는지는 해당 클래스와 이를 사용하는 클라이언트 간의 중요한 정보입니다. 이러한 정보가 API 문서에 포함되지 않으면 클래스 사용자는 이 관계를 추측해야 합니다. 그러나 이러한 추측이 틀릴 경우 클라이언트 프로그램은 동기화를 충분히 하지 못하거나 과도하게 동기화하여 성능 문제나 오류를 발생시킬 수 있습니다. 따라서 API 문서에 이러한 정보를 명시하는 것이 중요합니다. synchronized 한정자 API 문서에 synchronized 한정자가 있는 메서드는 안전하다고 간주하는 개발자들이 많은데, 이는 몇 가지 이유로 틀린 생각 메서드 선언에 synchronized 한정자를 선언할지는 구현 이슈일 뿐 API에 속하지 않기 때문에 Ja..

JAVA/Effective Java 2024.04.07

[아이템 81] wait와 notify보다는 동시성 유틸리티를 애용하라

wait(), notify(), notifyAll() 자바 5+ 버전에서 도입된 고수준의 동시성 유틸리티가 wait(), notify()로 하드 코딩해야 했던 일들을 대신 처리해 주기 때문에 현재는 wait()와 notify(), notifyAll() 메서드를 직접 호출해야 할 일이 많이 줄어듦 또한, wait()와 notify()는 올바르게 사용하기 까다롭기 때문에 java.util.concurrent의 고수준 동시성 유틸리티를 사용하는 것을 권장 고수준 동시성 유틸리티의 예는 다음과 같음 ExecutorService ConcurrentHashMap과 같은 Concurrent Collection Synchronizer 1. wait()와 notify()를 사용해야하는 케이스 새로운 코드라면 언제나 wa..

JAVA/Effective Java 2024.04.07

[아이템 80] 쓰레드보다는 실행자, 태스크, 스트림을 애용하라

실행자 프레임워크(Executor Framework) java.util.concurrent 패키지는 실행자 프레임워크(Executor Framework)라고 하는 인터페이스 기반의 유연한 태스크 실행 기능을 담고 있습니다. 과거에는 단순한 작업 큐(work queue)를 만들기 위해 수많은 코드를 작성했어야 했지만, 이제는 다음과 같이 간단하게 작업 큐를 생성할 수 있습니다. 1. 실행자 서비스(ExecutorService) 주요 기능 코드 부연 설명 get 메서드: 특정 태스크가 완료되기를 기다림 invokeAny 메서드: 태스크 모음 중 아무것 하나가 완료되기를 기다림 invokeAll 메서드: 모든 태스크가 완료되기를 기다림 awaitTermination 메서드: 실행자 서비스가 종료하기를 기다림 ..

JAVA/Effective Java 2024.04.07

[아이템 79] 과도한 동기화는 피하라

과도한 동기화는 다음과 같은 부작용을 초래합니다. 성능을 떨어뜨리고 Deadlock 상태에 빠드리고 심지어 예측할 수 없는 동작을 낳을 수 있음 응답 불가와 안전 실패를 피하려면 동기화 메서드나 동기화 블록 내 제어를 절대로 클라이언트에 양도하면 안 됩니다. 동기화된 클래스 관점에서, 다음과 같은 메서드들은 "외계인 메서드(alien method)"로 알려져 있는데, 이는 이러한 메서드들이 어떤 동작을 수행할지 확신할 수 없으며, 예외를 발생시키거나, 데드락 상태에 빠뜨리거나, 데이터를 손상시킬 수 있기 때문입니다. 동기화된 코드 블록 내 재정의 가능한 메서드 클라이언트가 전달한 함수 객체 외계인 메서드(Alien Method) 코드 부연 설명 관찰자들은 addObserver()와 removeObserv..

JAVA/Effective Java 2024.04.07

[아이템 78] 공유 중인 가변 데이터는 동기화해 사용하라

동기화 동기화란 멀티 쓰레드 환경에서 하나의 메서드 혹은 블록을 한 번에 하나의 쓰레드만 수행하도록 보장하는 것을 의미합니다. 싱글 쓰레드 환경에서는 동기화 걱정 안 해도 됨 멀티 쓰레드 환경에서는 여러 개의 쓰레드가 하나의 객체를 공유해서 사용하는 경우가 있으므로 동기화 처리 필요 1. 동기화 과정 한 객체가 일관된 상태를 가지고 생성되었을 때 해당 객체에 접근하는 메서드는 다른 쓰레드가 메서드를 실행할 수 없도록 락을 검 락을 건 메서드는 객체의 상태를 확인하거나 필요하면 수정 정리하자면 일관된 하나의 상태에서 다른 일관된 상태로 변화시킴 메서드 실행이 끝나면 락을 해제 2. 동기화 특징 동기화를 제대로 사용할 경우 어떤 메서드도 해당 객체의 상태가 일과되지 않은 순간을 목격할 수 없음 동기화가 없을..

JAVA/Effective Java 2024.03.29

[아이템 77] 예외를 무시하지 말라

API 설계자가 메서드 선언에 예외를 명시하는 이유는 적절한 조치가 필요하기 때문인데 많은 개발자들이 API 설계자의 목소리를 흘려버리고 있습니다. 아래 코드처럼 try문으로 감싼 뒤 catch 블록에서 아무 일도 하지 않는 코드들이 많음 코드 문제점 예외는 문제 상황에 잘 대처하기 위해 존재하는데 catch 블록을 비워두면 예외가 존재할 이유가 없어짐 운이 좋아 코드가 잘 돌아갈 수도 있지만, 적절한 예외 처리가 이루어지지 않으면 오동작할 가능성이 높아짐 따라서 빈 catch 블록은 절대적으로 피해야 함 예측할 수 있는 예외 상황이든 프로그래밍 오류든, 빈 catch 블록으로 못 본 척 지나치면 해당 프로그램은 오류를 내재한 채 동작 그러다 어느 순간 문제의 원인과 아무 상관없는 곳에서 갑자기 죽어버릴..

JAVA/Effective Java 2024.03.29

[아이템 76] 가능한 한 실패 원자적으로 만들라

실패 원자성 호출된 메서드가 예외 발생으로 인해 실패하더라도 객체가 메서드 호출 전 상태를 유지하는 특성 실패 원자성이 보장되면 Checked Exception을 던졌을 때 호출자가 오류 상태를 복구할 수 있으므로 유용함 메서드를 실패 원자적으로 만드는 방법은 총 네 가지가 있으며 다음과 같습니다. 불변 객체로 설계 매개변수 유효성 검사 복사본에 로직을 수행 후, 성공적으로 수행이 완료될 경우에만 원본 객체와 Swap 작업 도중의 에러를 가로채는 복구 코드를 작성하여 롤백 1. 불변 객체로 설계 불변 객체는 생성 시점에 고정되어 절대 변하지 않기 때문에 태생적으로 실패 원자적 메서드가 실패하면 새로운 객체가 생성되지 않을 수 있으나 기존 객체가 불안정한 상태에 빠지는 일은 없음 2. 매개변수 유효성 검사..

JAVA/Effective Java 2024.03.29