이 영역을 누르면 첫 페이지로 이동
codesche's blog 블로그의 첫 페이지로 이동

codesche's blog

페이지 맨 위로 올라가기

codesche's blog

chap13-동시성

  • 2023.04.07 21:00
  • Study Cafe/Clean Code 스터디

목차

  1. 동시성 프로그래밍이란
  2. 동시성 프로그래밍이 필요한 이유
  3. 안전한 동시성 프로그래밍 규칙
  4. 동시성 테스트 방법
  5. 오픈소스 속 동시성 처리

1. 동시성 프로그래밍이란

동시성 프로그래밍

  • 어플리케이션을 효율적으로 실행하기 위해 멀티코어를 온전히 활용하도록 구현하는 방식
  • 외부 서비스의 응답을 기다리면서 아무일도 하지 않으면 CPU 사이클이 낭비된다

동시성 프로그래밍 이해하기

image

동시성이 구현되지 않은 경우

image

병렬성을 구현한 경우

image

동시성을 구현한 경우

image

동시성과 병렬성이 구현된 경우

image

=> 클라이언트가 아닌 어플리케이션 관점에서 봐야 한다.

  • 내 어플리케이션의 효율성을 높이고 더불어 내 어플리케이션이 동작하는 머신의 환경이 효율적으로 돌아가도록 메모리 누수나 자원이 낭비되지 않도록 한다.

2. 동시성 프로그래밍이 필요한 이유

○ 동시성 프로그래밍의 미신과 오해 (1)

동시성은 항상 성능을 높여준다 (X)

동시성은 때로 성능을 높여준다 (O)

  • 대기 시간이 아주 길어 여러 스레드가 프로세서를 공유할 수 있거나, 여러 프로세서가 동시에 처리할 독립적인 계산이 충분히 많은 경우에만 성능이 높아진다.
  • 예시 : 웹 브라우저에서 여러 가지 이미지 리소스들을 불러와 다운로드 하는 경우
  • 예시 상황 :Servlet

Java Servlet 동시성 구현

image

  • 요청이 들어오면 Thread Pool에 있는 Thread가 서블릿의 service() 메서드를 호출한다.
  • service의 doGet(), doPost()에서 요청에 대한 처리를 하도록 구현한다.

image

○ 동시성 프로그래밍의 미신과 오해 (2)

동시성을 구현해도 설계는 변하지 않는다 (X)

동시성을 구현하면 설계를 바꿔야 한다 (O)

  • 단일 스레드 시스템과 다중 스레드 시스템은 설계가 판이하게 다르다.
  • '무엇'과 '언제'를 분리하면 시스템의 구조가 크게 달라진다.

○ 동시성 프로그래밍의 미신과 오해 (3)

Web이나 EJB와 같은 컨테이너를 사용한다면 동시성을 이해할 필요가 없다 (X)

컨테이너를 사용해도 동시성을 이해해야 한다 (O)

  • 어플리케이션이 컨테이너를 통해 멀티 쓰레드를 사용하는 것이기 때문에 컨테이너의 동작을 이해해야 한다.
  • 동시 수정, 데드락 같은 문제를 피할 수 있는지를 알아야 한다.

3. 안전한 동시성 프로그래밍 규칙

단일 책임 원칙(SRP) 설계

동시성 관련 코드는 다른 코드와 분리하라

  • 동시성 코드는 독자적인 개발, 변경, 조율 주기가 있다.
  • 동시성 코드에는 독자적인 난관이 있다. 다른 코드에서 겪는 난관과 다르며 훨씬 어렵다.
  • 잘못 구현한 동시성 코드는 다양한 방식으로 실패한다. 주변에 있는 다른 코드가 발목을 잡지 않더라도 동시성 하나만으로도 충분히 어렵다.

자료 범위를 제한하라

공유 자료를 최대한 줄여라

  • 동시 수정 문제를 피하기 위해 객체를 사용하는 코드내 임계영역을 synchronized 키워드로 보호하라.
  • 보호할 임계영역을 빼먹거나, 모든 임계영역을 보호했는지 확인하는 수고가 필요하기에 임계 영역의 수를 최소화 해야 한다.

자료 사본을 사용하라

공유 자료를 줄이려면, 최대한 공유하지 않는 방법이 제일 좋다

  • 객체를 복사해 읽기 전용으로 사용한다.
  • 각 스레드가 객체를 복사해 사용한 후 한 스레드가 해당 사본에서 결과를 가져온다.
  • 사본을 사용하는 방식으로 내부 잠금을 없애 수행 시간을 절약하는 것이 사본 생성과 가비지 컬렉션에 드는 부하를 상쇄할 가능성이 크다.

Thread는 가능한 독립적으로 구현하라

다른 스레드와 자료를 공유하지 않는다

  • 서블릿처럼 각 Thread는 클라이언트 요청 하나를 처리한다.
  • 모든 정보는 비공유 출처(client의 request)에서 가져오며 로컬 변수에 저장한다.
  • 각 서블릿은 마치 자신이 독자적인 시스템에서 동작하는 양 요청을 처리한다.

라이브러리를 이해하라

java.util.concurrent 패키지를 익혀라

image

  • Thread Safe한 컬렉션을 사용한다. ConcurrentHashMap, AtomicLong
  • 서로 무관한 작업을 수행할 때는 executor 프레임워크를 사용한다.
  • 가능하다면 Thread가 Blocking되지 않는 방법을 사용한다.

동기화하는 메서드 사이에 존재하는 의존성을 이해하라

공유 객체 하나에는 메서드 하나만 사용하라

  • 클라이언트에서 잠금 - 클라이언트에서 첫 번째 메서드를 호출하기 전에 서버를 잠근다. 마지막 메서드를 호출할 때까지 잠금을 유지한다.
  • 서버에서 잠금 - 서버에다 "서버를 잠그고 모든 메서드를 호출한 후 잠금을 해제하는" 메서드를 구현한다. 클라이언트에서는 이 메서드를 호출하기만 하면 된다.
  • 연결(Adapter) 서버 - 잠금을 수행하는 중간 단계를 생성한다. '서버에서 잠금' 방식과 유사하지만 원래 서버는 변경하지 않는다.

문제 상황: 공유 자원 접근

image

Client Based Lock

image

Server Based Lock

image

Adapter Based Lock

image

4. 동시성 테스트 방법

동시성 코드를 테스트해야 한다

테스트를 했다고 동시성 코드가 100% 올바르다고 증명하기는 불가능하다. 하지만 충분한 테스트는 위험을 낮춰준다.

  • 문제를 노출하는 테스트 케이스를 작성하라
  • 프로그램 설정과 시스템 설정과 부하를 바꿔가며 자주 돌려라
  • 테스트가 실패하면 원인을 추적하라
  • 다시 돌렸더니 통과한다는 이유로 그냥 넘어가면 절대로 안 된다

코드에 보조 코드를 넣어 돌려라

드물게 발생하는 오류를 자주 발생시키도록 보조 코드를 추가한다

  • 코드에 wait(), sleep(), yield(), priority() 함수를 추가해 직접 구현한다.
  • 보조코드를 넣어주는 도구를 사용해 테스트한다.
    • 다양한 위치에 ThreadJigglePoint.jiggle()을 추가해 무작위로 sleep(), yield()가 호출되도록 한다.
  • 테스트 환경에서 보조코드를 돌려본다.

동시성 코드를 실제 환경이나 테스트 환경에서 돌려본다

다양한 요청과 상황에 동시성 코드가 정상적으로 동작하는지 확인한다

  • 배포하기 전에 테스트 환경에서 충분히 오랜시간 검증한다.
  • 동시성 코드를 배포한 후에 모니터링을 통해 문제가 발생하는지 확인한다.

5. 오픈 소스 속 동시성 처리

Elastic Search - LockingAtomicCounter

image

image

image

image

image

image

'Study Cafe > Clean Code 스터디' 카테고리의 다른 글

chap15 & chap16-라이브러리 분석을 통해 코드를 바라보는 시각 기르기  (0) 2023.04.09
chap14-점진적인 개선  (0) 2023.04.07
chap12-창발적 설계  (0) 2023.03.31
chap11-관심사 분리 패턴들  (0) 2023.03.30
chap10-클래스 잘 설계하기  (0) 2023.03.29

댓글

이 글 공유하기

  • 구독하기

    구독하기

  • 카카오톡

    카카오톡

  • 라인

    라인

  • 트위터

    트위터

  • Facebook

    Facebook

  • 카카오스토리

    카카오스토리

  • 밴드

    밴드

  • 네이버 블로그

    네이버 블로그

  • Pocket

    Pocket

  • Evernote

    Evernote

다른 글

  • chap15 & chap16-라이브러리 분석을 통해 코드를 바라보는 시각 기르기

    chap15 & chap16-라이브러리 분석을 통해 코드를 바라보는 시각 기르기

    2023.04.09
  • chap14-점진적인 개선

    chap14-점진적인 개선

    2023.04.07
  • chap12-창발적 설계

    chap12-창발적 설계

    2023.03.31
  • chap11-관심사 분리 패턴들

    chap11-관심사 분리 패턴들

    2023.03.30
다른 글 더 둘러보기

정보

codesche's blog 블로그의 첫 페이지로 이동

codesche's blog

  • codesche's blog의 첫 페이지로 이동

검색

메뉴

  • 홈
  • 태그
  • 방명록

카테고리

  • 분류 전체보기 (76)
    • Algorithm (15)
      • 백준 (3)
      • 프로그래머스 (10)
      • inflearn 알고리즘(Java) (2)
    • 블로그소개 (1)
    • Back-End (11)
      • Java (10)
      • SpringBoot (1)
    • Database (2)
      • MySQL (0)
      • MariaDB (1)
      • Redis (0)
      • 개념, 이론 (1)
    • Front-End (0)
      • html, css, javascript (0)
    • Git (2)
    • 알고리즘 지식 (11)
      • 자료구조 (11)
    • Study Cafe (21)
      • 기술면접 (6)
      • Clean Code 스터디 (14)
      • CS 스터디 (0)
      • 개발용어 (1)
    • 주간 에세이 (10)
    • DevOps (3)
      • 배포, Front&Back 연동 (1)
      • AWS (0)
      • Docker (1)
      • 이론 (1)

최근 글

인기 글

댓글

공지사항

아카이브

태그

  • 개발자 현실
  • 자바 변수
  • git commit
  • 자바 기초
  • 주간에세이
  • 클린코드
  • 자료구조
  • java

나의 외부 링크

정보

The Code의 codesche's blog

codesche's blog

The Code

블로그 구독하기

  • 구독하기
  • RSS 피드

방문자

  • 전체 방문자
  • 오늘
  • 어제

티스토리

  • 티스토리 홈
  • 이 블로그 관리하기
  • 글쓰기
Powered by Tistory / Kakao. © The Code. Designed by Fraccino.

티스토리툴바