이번 장에는 책에서 반복적으로 다뤄지는 내용들이 많이 포함되어 있었다.

6장. 동시성

Java에서의 동시성 제어

synchronized (모니터)

Lock

자바에 이렇게 락이 많은지 처음알았다. 아래말고도 더 있다

  • Lock
  • ReadWriteLock
  • ReentrantLock
  • ReentrantReadWriteLock
  • StampedLock
    • 낙관적 읽기
    • 쓰기 락
    • 읽기 락

Semaphore

  1. 세마포어에서 퍼밋 획득 (P 연산 → wait)
  2. 코드 실행
  3. 퍼밋 반환 (V 연산 → signal)

Atomic 타입들

기본적으로 CAS(Compare And Swap) 방식으로 구현되어 있음

(CAS를 이용한 Lock-Free 자료구조 만들어보기)

DB와 동시성

비관적 락 (선점 락)

실패할 가능성이 높을 때 사용한다.

트랜잭션이 select ~ for update 로 먼저 레코드에 대한 잠금을 획득한다.

낙관적 락

성공할 가능성이 높을 때 사용한다.

데이터를 조회한 시점의 값과 수정하려는 시점의 값이 같은지 비교하는 방식으로 동시성 문제 처리. 보통 정수 타입의 버전 컬럼을 사용한다.

구현 방법

  1. 버전 조회
  2. 로직 수행
  3. (1)에서 조회한 값과 같은지 비교하는 WHERE 절을 추가한 버전을 증가하는 UPDATE 쿼리 실행
  4. UPDATE 결과로 변경된 행 개수에 따라 커밋 or 롤백 실행

증분 쿼리

잠금을 사용하지 않으면서 수를 증가시키는 방법.

UPDATE subject SET join_count = join_count + 1 WHERE id = ?

DB가 join_count = join_count + 1 을 원자적 연산으로 처리한다 → 사용하는 DB에 따라 다를 수 있다.

잠금 사용 시 주의 사항

  • 잠금을 획득한 뒤에는 반드시 잠금을 해제
  • 잠금 획득을 시도할 때, 대기 시간 지정하기 (잠금 획득 제한 시간)
  • 데드락 피하기
    • 데드락 4가지 필요조건
      • Mutual exclusion
      • Hold and wait
      • Circular wait
      • No preemption
    • 잠금 대기 시간 제한하기 → 트랜잭션 중단으로 교착을 제거/회피 전략
    • 지정한 순서대로 잠금 획득 → Circular wait을 없애기

라이브락(livelock)

교착과 달리 시스템이 계속 상태 전이를 수행하지만, 특정 진행 조건을 영원히 만족하지 못하고 무한히 실행되는 것.

책에서 다음 예시를 들었다.

  • 길에서 두 사람이 마주 보며 걸어오다 마주침 → 피하려고 둘 다 옆으로 이동 → 계속 마주 보고 있음 → 다시 둘 다 옆으로 이동 → 여전히 마주치고 있는 상태
  • 철학자의 만찬 문제

우선 순위를 두는 방식으로 라이브락을 해소할 수 있다.

단일 스레드로 처리 고려

다중 스레드가 동시에 처리하던 것을 단일 스레드로 처리하는 방법도 고려해봐야 한다.

고려해봐야 하는 요소들

  • 임계 영역의 실행 시간
  • 동시에 접근하는 스레드 수
  • 잠금 획득/해제에 소요되는 시간

 


7장. IO 병목

가상 스레드

가상 스레드가 나오면서 기존 스레드 네이밍 → Platform thread

가상 스레드를 실행하는 플랫폼 스레드 = 캐리어 스레드

  • 가상 스레드 매핑 → 플랫폼 스레드에 마운트 되었다 → 실행

Java 23 이하에서는 가상 스레드가 synchronized 내부에서는 플랫폼 스레드에서 언마운트되지 않아 블로킹되는 한계가 존재함 (Pinning 이슈) ⇒ Java 24 (JEP 491)에서 해소됨

가상 스레드는 기존 자바 코드를 크게 바꾸지 않으면서 사용할 수 있기 때문에, 소켓 통신에서 우선 가상 스레드로 바꿔보고 그래도 사용자가 너무 많으면 논블로킹 I/O로 가자고 저자가 말했다.

 

[참고자료]

각종 풀…?

풀에 있는 각각의 워커들은 플랫폼 스레드

  • ForkJoinPool
  • Tomcat Thread Pool
  • @Async Pool
  • @Scheduled Pool

I/O 멀티플렉싱

하나(또는 소수)의 스레드가 여러 I/O 소켓/파일 디스크립터를 동시에 감시하다가, 읽거나 쓸 준비가 된 것들만 골라서 처리하는 방식

⇒ 게임 서버에서 잘 사용되는 것으로 알고 있음

  • epoll (리눅스)
  • IOCP (윈도우)

리액터 패턴

  • 리액터 — 이벤트를 대기하고 핸들러에 전달하는 과정 반복 (이벤트 루프)
  • 핸들러

 

 

 

I/O 멀티플렉싱과 리액터 패턴은 직접적으로 사용해본 적이 없어서 잘 모르겠다. 더 많은 공부가 필요하다… 지금은 용어 정도만 알아두고 나중에 돌아와서 다시 공부해보도록 하자