일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- mysql
- 쿠키
- 인터페이스
- 선형 리스트
- 코드
- @ComponentScan
- 마크다운
- query
- 마크다운 테이블
- 리스트
- 정렬
- code
- 클린코드
- 클래스
- CleanCode
- 자료구조
- java
- 계산 검색 방식
- WebClient
- @NoArgsConstructor
- 클린
- 트리
- 스택 큐 차이
- 쿼리메소드
- 내부 정렬
- @RequiredArgsConstructor
- 연결 리스트
- JsonNode
- 빅 오 표기법
- 배열
- Today
- Total
Developer Cafe
클린코드 13장 동시성 본문
동시성이 필요한 이유
한 유저의 요청을 처리하는 데에 1초가 필요한 시스템을 생각해 보자. 이 시스템은 적은 유저가 사용할 경우 그럭저럭 괜찮은 퍼포먼스를 보여줄 것이다. 하지만 유저가 늘어남에 따라 모든 유저는 자신보다 먼저 도착한 요청이 끝날 때까지 기다려야만 한다. 이러한 경우 병행성(concurrency)이 여러 유저를 동시에 처리함으로써 처리량을 향상시킬 수 있다.
/* Code 1-1 */
public class ClassWithThreadingProblem {
private int lastIdUsed;
public ClassWithThreadingProblem(int lastIdUsed) {
this.lastIdUsed = lastIdUsed;
}
public int getNextId() {
return ++lastIdUsed;
}
}
public static void main(String args[]) {
final ClassWithThreadingProblem classWithThreadingProblem = new ClassWithThreadingProblem(42);
Runnable runnable = new Runnable() {
public void run() {
classWithThreadingProblem.getNextId();
}
};
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
t1.start();
t2.start();
}
위 코드가 만들 수 있는 결과는 총 3가지 이다.
- t1이 43을, t2가 44를 가져간다. lastIdUsed는 44이다.(O)
- t1이 44을, t2가 43를 가져간다. lastIdUsed는 44이다.(O)
- t1이 43을, t2가 43를 가져간다. lastIdUsed는 43이다.(X)
위의 getNextId() 메서드는 8개의 자바 byte-code로 변환되며, 이를 두 스레드에서 실행하게 되면 총 12,870개의 코드 조합을 낼 수 있다. 그 중 얼마 안 되는 몇몇 조합이 위의 3가지 결과 중 마지막 결과를 낳게 된다.
실행 모델을 이해하라
Name | Description |
Bound Resources | Concurrent 환경에서 사용되는 고정된 크기의 자원이다. 예시로 데이터베이스 연결, 고정된 크기의 읽기/쓰기 버퍼가 있다. |
Mutual Exclusion | 한 시점에 공유 자원에 접근할 수 있는 스레드는 단 하나이다. |
Starvation | 한 스레드 혹은 스레드의 그룹이 긴 시간 혹은 영원히 작업을 수행할 수 없게 된다. 작업의 우선권을 가지는 수행 시간이 짧은 스레드가 끝없이 실행된다면 수행 시간이 긴 스레드는 굶게 된다. |
Deadlock | 두 개 이상의 스레드들이 서로의 작업이 끝나기를 기다린다. 각 스레드는 서로가 필요로 하는 자원을 점유하고 있으며 필요한 자원을 얻지 못하는 이상 그 누구도 작업을 끝내지 못하게 된다. |
Livelock | 스레드들이 서로 작업을 수행하려는 중 다른 스레드가 작업중인 것을 인지하고 서로 양보한다. 이러한 공명 때문에 스레드들은 작업을 계속 수행하려 하지만 장시간 혹은 영원히 작업을 수행하지 못하게 된다. |
동기화하는 메서드 사이에 존재하는 의존성을 이해하라
동기화된 메서드 간의 의존성은 concurrent 코드에서 사소한 버그를 일으킬 수 있다. 자바는 synchronized라는 "메서드 하나를 보호하는 노테이션"을 제공한다. 하지만 한 클래스에 두 개 이상의 synchronized 메서드가 존재하면 문제를 일으킬 수도 있다.
추천: 공유된 객체의 두 메서드 이상을 사용하는 것을 피하라.
만약 위 추천을 따를 수 없는 상황이라면 아래의 세 방법을 고려해 보라.
클라이언트 기반 잠금(Client-Based Locking): 클라이언트가 첫 메서드를 부르기 이전부터 마지막 메서드를 부른 다음까지 서버를 잠근다. (역주: 공유 객체를 사용하는 코드에서 공유 객체를 잠그는 것이다.)
=> Bad: 서버를 사용하는 모든 클라이언트 코드에서 lock이 필요하게 되며 이는 유지보수 및 디버깅에 필요한 비용을 상승시킨다.
서버 기반 잠금(Server-Based Locking): 서버 내에서 서버(자신)을 잠그고 모든 동작을 수행한 후 잠금을 푸는 메서드를 제공한다. 클라이언트에게는 새로운 메서드를 제공한다. (역주: 공유 객체에 새로운 메서드를 작성하고 잠금이 필요한 동작 전체를 수행하게 하는 것이다.)
=> Good: 임계구역(Critical section)에 접근하는 코드를 최소화해 위 4-2에 부합한다.
임계 구역 또는 공유변수 영역은 병렬컴퓨팅에서 둘 이상의 스레드가 동시에 접근해서는 안되는 공유 자원을 접근하는 코드의 일부를 말한다
중계된 서버(Adapted Server): 잠금을 수행하는 중계자를 작성한다. 이는 기본적으로 서버 기반 잠금이지만 기존의 서버를 변경할 수 없는 상황에 사용할 수 있는 방법이다.(역주: 서드 파티 라이브러리를 사용한다고 생각하면 쉬울 것이다.)
'책을읽읍시다' 카테고리의 다른 글
[그림으로 배우는 HTTP & Network Basic] 프록시(Proxy)와 게이트웨이(Gateway) 그리고 터널(Tunnel) (0) | 2022.10.27 |
---|---|
클린코드 12장 창발성 (0) | 2022.09.15 |
클린코드 10장 클래스 (0) | 2022.09.15 |
클린코드 9장 단위 테스트 (0) | 2022.09.15 |
클린코드 7장 에러 핸들링 (0) | 2022.09.05 |