개발/[스터디] 이펙티브 자바

[이펙티브자바]77-81

Dahee Joy Cha 2022. 3. 20. 15:46

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

1. API 설계자가 메서드 선언에 예외를 명시하는 까닭

그 메서드를 사용할 때 적절한 조치를 취해달라고 말하는 것이다. 

// catch 후 아무것도 하지 않는 코드
try {
	...
} catch (SomeException e) {

}

catch 블록을 비워두면 예외가 존재할 이유가 없다. 

 

2. 예외를 무시하기로했다면?

catch 블록 안에 그렇게 결정한 이유를 주석으로 남기고, 예외 변수의 이름도 ignored로 바꿔놓자.

 

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

synchronized 키워드

메서드나 블록을 한 번에 한 스레드씩 수행하도록 보장 

 

동기화

- (1)배타적 실행 (2)스레드 사이의 안정적인 통신 보장

- 쓰기와 읽기 모두를 동기화하여 동작을 보장하자. 

   : 코드 78-2(416p)에서는 requestStop(읽기)과 stopRequested(쓰기) 둘 다 동기화 하였다. 

 

가변 데이터로 인한 이슈를 막으려면

가변 데이터는 단일 스레드에서만 쓰도록 하자. 

 

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

과도한 동기화의 단점

(1) 성능 저하 (2) 교착 상태 발생 (3) 예측할 수 없는 동작 발생

 

응답 불가와 안전 실패를 피하려면

- 동기화 메서드/동기화 블럭 안에서는 제어를 클라이언트에 양도하면 안 된다.

   => 에일리언 함수(alien method)를 사용하지 말자.

 

* 용어 설명

1. 응답불가

exception, timeout, lock등의 상황

2. 안전 실패(safety failure = fail safe): 실패시에도 작업을 중단하지 않는다

   cf. Fail fast: 가능한 빨리 실패를 노출하고, 작업을 중지한다. 

3. 외계인 메서드 (alien method)

무슨 일을 할지 모르는, 통제 불가능한 함수. 

예외를 일으키거나, deadlock이 발생하거나, data가 훼손될 수 있다. 

 

동기화 영역에서는 가능한 일을 적게 하자. 

동기화는 성능 저하가 일어난다. 동기화가 꼭 필요한 곳 적용하라. 

 

결론

- 동기화가 가지고 있는 위험성에 대해 올바르게 이해하고 사용하자. 

- 합당한 이유가 있을 때만 동기화하자. 

 

아이템 80. 스레드보다는 실행자, 테스크, 스트림을 이용하라

아이템 49의 작업 큐(work queue) 문제점

- 클라이언트가 요청한 작업을 백그라운드 스레드에 위임. 비동기적으로 처리

- 안전 실패/응답 불가를 처리하기 위해 작성해야하는 코드 많았다. 

 

java.util.concurrent 패키지의 등장

- 이 패키지는, 실행자 프레임워크(Executor Framework)라고 하는 인터페이스 기반의 유연한 테스크 실행 담고 있다. 

- 작업 큐를 편리하게 사용 가능

ExecutorService exec = Executors.newSingleThreadExecutor(); // 생성
exec.execute(runnable); // 실행할 task 넘기기
exec.shutdown(); // 종료

 

실행자 프레임워크의 주요기능

- 특정 테스크가 완료되길 기다림

- 테스크 모음중 랜덤하게 하나가 종료되길 (invokeAny), 혹은 모든 테스크가 종료되길(invokeAll) 기다림

- 실행자 서비스가 종료하길 기다림(awaitTermination)

- 완료된 테스트 결과 차례로 받음 (ExecutorCompletionService)

- 테스크를 특정 시간에or주기적으로 실행하게 한다. (ScheduledThreadPoolExecutor)

 

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

1. wait, notify 를 게속 사용해야할까

대체할 방법이 있다.

: 아이템 50에서 올바르게 사용하는 방법을 안내했으나, 자바5에서 도입된 고수준의 유틸리티가 wait/notify로 처리해야할 까다로운 작업들을 대신해준다. 

 

2. java.util.concurrent

Executor framework (실행자 프레임워크)

Concurrent collection (동시성 컬렉션)

Synchronizer (동기화 장치)

 

2.1 동시성 컬렉션

- 표준 컬렉션 인터페이스(List, Queue, Map)에 동시성을 추가

- 동기화를 내부에서 수행. 외부에서 Lock을 걸면 느려진다. 

 

2.1.1 상태 의존적 메서드

- 동시성 컬렉션의 동시성을 무력화하지 못하기 때문에, 여러 메서드를 원자적으로 호출하는 것은 불가능하다. 

  => 따라서, 여러 동작을 하나의 원자적 동작으로 묶어 제공하는 상태 의존적 메서드 탄생 (예. 코드 81-2)

 

2.1.2 동기화한 컬렉션보다는 동시성 컬렉션을 사용하자. 

synchronizedMap보다는 concurrentHashMap을 사용하자.

->TODO: 내부 동작 코드 비교해보기. 

 

2.2 동기화장치

스레드가 다른 스레드 기다리도록 한다. 

CountDownLatch, Semaphore, CyclicBarrier, Exchanger, Phaser

 

3. wait와 notify 를 꼭 사용해야할 경우

반복문/동기화 영역 안에서 사용하라.