이번 주 목차
13장 동시성
14장 점진적인 개선
15장 JUnit 들여다보기
16장 SerialDate 리팩터링
13장 동시성
- 동시성이 필요한 이유?
동시성은 결합을 없애는 전략이다. 즉 무엇과 언제를 분리한다.
다음은 동시성과 관련한 일반적인 미신과 오해다.
1. 동시성은 항상 성능을 높여준다. (때로 성능을 높여준다. 대기 시간이 아주 길어 여러 스레드가 프로세서를 공유할 수 있거나, 여러 프로세서가 동시에 처리할 독립적인 계산이 충분히 많은 경우에만 성능이 높아진다.)
2. 동시성을 구현해도 설계는 변하지 않는다. (단일 스레드 시스템과 다중 스레드 시스템은 설계가 다르다.)
3. 웹 또는 EJB 컨테이너 등을 사용하면 동시성을 이해할 필요가 없다. (실제로는 컨테이너가 어떻게 동작하는지, 동시 수정, 데드락 등과 같은 문제를 피할 수 있는지를 알아야한다.)
반대로 다음은 동시성과 관련된 타당한 생각 몇 가지다.
1. 동시성은 다소 부하를 유발한다. (성능 측면에서 부하가 걸리며, 코드도 더 짜야 한다.)
2. 동시성은 복잡하다. (간단한 문제라도 동시성은 복잡하다.)
3. 일반적으로 동시성 버그는 재현하기 어렵다.(그래서 진짜 결함으로 간주되지 않고 일회성으로 무시하기 쉽다.)
4. 동시성을 구현하려면 흔히 근본적인 설계 전략을 재고해야 한다.
- Clean Code 228 Page 인용 -
- 동시성 방어 원칙
권장사항 :
1. 동시성 코드는 다른 코드와 분리하라.
2. 자료를 캡슐화하라.
3. 독자적인 스레드로, 가능하면 다른 프로세서에서 돌려도 괜찮도록 자료를 독립적인 단위로 분할하라.
- Clean Code 230 Page 인용 -
- 라이브러리를 이해하라
- 스레드 환경에 안전한 컬렉션을 사용한다. 자바 5부터 제공한다.
- 서로 무관한 작업을 수행할 대는 executor 프레임 워크를 사용한다.
- 가능하다면 스레드가 차단(blocking) 되지 않는 방법을 사용한다.
- 일부 클래스 라이브러리는 스레드에 안전하지 못하다.
+ 언어가 제공하는 클래스를 검토하라.
- Clean Code 232 Page 인용 -
사실 아직도 라이브러리를 다 이해하지 못했다.
사람들이 계속해서 토이프로젝트를 하라는 이유가 라이브러리를 한층 더 이해하기 쉬워서 그런것인가 하는 생각도 든다.
- 실행 모델을 이해하라
다중 스레드 애플리케이션 분류 방식은 여러가지이다. 기본 용어부터 이해하자.
한정된 자원 | 다중 스레드 환경에서 사용하는 자원으로, 크기나 숫자가 제한적이다. 데이터베이스 연결, 길이가 일정한 읽기/쓰기 버퍼 등이 예시이다. |
상호 배제 | 한 번에 한 스레드만 공유 자료나 공유 자원을 사용할 수 있는 경우를 가리킨다. |
기아 | 한 스레드나 여러 스레드가 굉장히 오랫동안 혹은 영원히 자원을 기다린다. 예를 들어, 항상 짧은 스레드에게 우선순위를 준다면 짧은 스레드가 지속적으로 이어질 경우, 긴 스레드가 기아 상태에 빠진다. |
데드락 | 여러 스레드가 서로가 끝나기를 기다린다. 모든 스레드가 각기 필요한 자원을 다른 스레드가 점유하는 바람에 어느쪽도 더 이상 진행하지 못한다. |
라이브락 | 락을 거는 단계에서 각 스레드가 서로를 방해한다. 스레드는 계속해서 진행하려 하지면, 공명으로 인해 굉장히 오랫동안 혹은 영원히 진행하지 못한다. |
생상자-소비자
하나 이상 생산자 스레드가 정보를 생성해 버퍼나 대기열에 넣는다. 생산자 스레드와 소비자 스레드가 사용하는 대기열은 한정된 자원이다. 즉, 빈 공간이 있어야 일을 한다. 잘못하면 자원이 널널함에도 둘 다 서로의 작업이 끝나길 기다릴 가능성이 존재한다.
읽기-쓰기
읽기 스레드의 요구와 쓰기 쓰레드의 요구를 처리율도 높이고 기아도 방지하는 해법이 필요하다. 간단한 전략은 읽기 스레드가 없을 때까지 갱신을 원하는 쓰기 스레드가 버퍼를 기다리는 방법이다. 하지만 읽기 스레드가 계속 작업중이라면 쓰기 스레드는 기아 상태에 빠진다. 양쪽 균형을 잡으면서 동시 갱신 문제를 피하는 방법이 필요하다.
식사하는 철학자들
스레드와 자원의 균형! 주의해서 설계하지 않으면 데드락, 라이브락, 처리율 저하(효율성 저하) 등을 겪는다.
- 동기화하는 메서드 사이에 존재하는 의존성을 이해하라
공유 객체 하나에는 메서드 하나만 사용하라
만약 공유 객체 하나와 여러 메서드가 필요한 상황이라면 아래 3가지 상황을 고려하자.
클라이언트에서 잠금 - 클라이언트가 첫 메서드를 호출하기 전 서버를 잠근다. 마지막 메서드를 호출할 때까지 잠금을 유지한다.
서버에서 잠금 - "서버를 잠금고 모든 메서드를 호출한 후 잠금을 해제" 하는 메서드를 구현, 클라이언트가 이 메서드를 호출한다.
연결 서버 - 잠금을 수행하는 중간 단계를 생성한다. 원래 서버는 변경하지 않는다.
- 동기화하는 부분을 작게 만들어라
동기화하는 부분을 최대한 작게 만들어라. 코드를 짤때 임계영역 수를 최대한으로 줄이자!
- 올바른 종료 코드는 구현하기 어렵다
종료 코드를 초기부터 구현하자! 이미 나온 알고리즘을 검토하는 것도 좋은 방안이다.
- 스레드 코드 테스트하기
문제는 노출하는 테스트 케이스를 작성하자! 프로그램 설정과 시스템 설정과 부하를 바꿔가며 자주 돌리자. 테스트가 실패하면 원인을 추적하고, 다시 돌렸더니 통과한다라는 이유로 그냥 넘어가면 절대로 안된다.
13장 동시성 내 맘대로 정리
마지막 스레드 코드 테스트하기의 마지막 말이 마음에 걸린다. 나는 종종 다시 톰캣을 재구동을 했더니 오류가 안난다는 이유로 넘어간 적이 많았는데... 이미 지나간일 앞으로는 넘어가지 말아야겠다.
주의하지 않으면 희귀하고 오묘한 오류에 직면하게 된다.
이 말도 내가 자주 하는 말이다... 나는 특이한 오류를 많이 겪는다. 동시성때문인가?
책에서는 SRP를 준수하라고 한다.
POJO를 사용해 스레드를 아는 코드와 스레드를 모르는 코드를 분리하고 스레드 코드를 테스트할 대는 전적으로 스레드만 테스한다. 즉 스레드 코드는 최대한 집약되고 작아야 한다고 한다.
추가로! 스레드 코드는 많은 플랫폼에서 많은 설정으로 반복해서 계속 테스트해야한다.
14장 점진적인 개선
- Args(새로짠 유틸리티) 구현
프로그래밍은 과학보다 공예에 가깝다. 깨끗한 코드를 짜려면 먼저 지저분한 코드를 짠 뒤에 정리해야 한다는 의미이다.
무조건 돌아가는 프로그램을 목표로 잡지말고, 일단 프로그램이 돌아가면 다음 업무로 넘어가지 말자.
- Args: 1차 초안
프로그램을 망치는 가장 좋은 방법 중 하나는 개선이라는 이름 아래 구조를 크게 뒤집는 행위이다.
코드 작성 중 기능을 더 이상 추가하지 않고 리팩터링을 진행하라.
- String 인수
단계적으로 조금씩 변경하며 매번 테스트를 돌려야 하므로 코드를 여기저기 옮길 일이 많아진다. 리팰터링은 루빅큐브 맞추기와 비슷하다. 큰 목표 하나를 이루기 위해 자잘한 단계를 수없이 거친다.
14장 점진적인 개선 내 맘대로 정리
처음부터 코드를 깨끗하게 유지하기란 상대적으로 쉽다.
언제나 최대한 깔끔하고 단순하게 정리하자. 절대로 썩어나가게 방치하지 말자!
15장 JUnit 들여다보기
- JUnit 프레임워크
junit.framework.comparisonCompactor | 두 문자열을 받아 차이를 반환 | ABCDE / ABXDE >>> AB[X]DE |
15장 JUnit 들여다보기 내 맘대로 정리
리팩토링 하는 과정을 설명!
16장 SerialDate 리팩터링
SerialDate : 날짜를 표현하는 자바 클래스
- 첫째, 돌려보자
- 둘째, 고쳐보자
16장 SerialDate 리팩터링 내 맘대로 정리
리팩토링 하는 과정을 설명!
'STUDY REVIEW > 클린코드 독서스터디' 카테고리의 다른 글
Clean Code - 4주차 (0) | 2021.05.24 |
---|---|
Clean Code - 3주차 (0) | 2021.05.21 |
Clean Code - 2주차 스터디 후기 (0) | 2021.05.11 |
Clean Code - 2주차 (0) | 2021.05.10 |
Clean Code - 1주차 스터디 후기 (0) | 2021.05.03 |