|
| 1 | +**목차** |
| 2 | + |
| 3 | +- 1.1. 작업을 동시에 실행하는 일에 대한 간략한 역사 |
| 4 | +- 1.2. 스레드의 이점 |
| 5 | +- 1.3. 스레드 사용의 위험성 |
| 6 | +- 1.4. 스레드는 어디서나 |
| 7 | + |
| 8 | +## 1. 작업을 동시에 실행하는 일에 대한 간략한 역사 |
| 9 | + |
| 10 | +- 초기 컴퓨터: 운영체제 없음 → 극도로 비효율적 |
| 11 | +- **여러 프로그램을 동시에 실행할 수 있는 운영체제를 개발하게 된 요인** |
| 12 | + |
| 13 | + : 자원 활용, 공정성, 편의성 |
| 14 | + |
| 15 | + → 여러 프로그램을 "각자의 프로세스"에서 동시에 실행 가능 |
| 16 | + |
| 17 | +- 스레드로 인해 한 프로세스 안에 여러개의 프로그램 제어 흐름이 공존 가능함 |
| 18 | +- 한 프로세스 내 **모든 스레드는 동일한 heap에 객체를 할당하여 데이터 공유 가능** |
| 19 | + |
| 20 | + → 데이터에 대한 적절한 동기화가 필요 |
| 21 | + |
| 22 | + |
| 23 | +## 2. 스레드의 이점 (31p ~ ) |
| 24 | + |
| 25 | +- 서버 애플리케이션에 성능(자원 활용도와 처리율) 향상, 유지보수 비용 감소 |
| 26 | + |
| 27 | +### 2.1. 멀티프로세서 활용 |
| 28 | + |
| 29 | +- 프로세서가 여러 개인 경우, 단일 스레드 프로그램은 자원 낭비 |
| 30 | +- 스레드를 여러 개 사용하면 I/O 대기 시간 동안 CPU 활용 가능 |
| 31 | + |
| 32 | +### 2.2. 단순한 모델링 |
| 33 | + |
| 34 | +- 작업 단위를 스레드로 나눠서 구조를 명확하게 유지 |
| 35 | +- 프레임워크(Servlet, RMI 등)가 스레드를 관리 → 개발자는 요청 처리 코드만 작성하면 됨 |
| 36 | + |
| 37 | +### 2.3. 단순한 비동기 이벤트 처리 |
| 38 | + |
| 39 | +- 클라이언트 연결마다 스레드 할당 시, I/O 병목 해결 쉬움 |
| 40 | +- 과거에는 NIO 기반 비동기 코드 필요 → 지금은 다중 스레딩이 일반적 |
| 41 | + |
| 42 | +### 2.4. 더 빨리 반응하는 사용자 인터페이스 |
| 43 | + |
| 44 | +- UI 작업을 별도 스레드로 처리하면 반응성 향상 |
| 45 | +- 오래 걸리는 작업이 UI 스레드를 막는 문제 해결 가능 |
| 46 | + |
| 47 | +## 3. 스레드 사용의 위험성 |
| 48 | + |
| 49 | +- 스레드 사용으로 인한 “병렬 문제”에 대한 이해도가 필요 |
| 50 | + |
| 51 | +### 3.1. 안전성 위해 요소 |
| 52 | + |
| 53 | +- 스레드가 공유 자원에 접근할 경우, 동기화가 필수 |
| 54 | +- 동기화 안 하면 실행 순서 예측 불가 & 이상 동작 가능 |
| 55 | +- `synchronized`로 동기화 → 명령어 순서/가시성 보장 |
| 56 | +- `@ThreadSafe` 같은 어노테이션으로 명시 가능 |
| 57 | + |
| 58 | +### 3.2 활동성 위험 |
| 59 | + |
| 60 | +- **안전성**: 잘못된 일이 발생하지 않는다 |
| 61 | +- **활동성**: 원하는 일이 결국 일어난다 |
| 62 | +- 문제 예: 데드락, 기아상태(Starvation), 라이브락 |
| 63 | +- 타이밍에 따라 발생 → 재현 어렵고 디버깅 난이도 높음 |
| 64 | + |
| 65 | +### 3.3 성능 위험 |
| 66 | + |
| 67 | +- 스레드 사용 시 컨텍스트 스위칭 비용, 캐시 무효화 등으로 성능 저하 가능 |
| 68 | +- 동기화로 인해 컴파일러 최적화 저해, 캐시 일관성 비용 발생 |
| 69 | + |
| 70 | +## 4. 스레드는 어디서나 |
| 71 | + |
| 72 | +- 모든 자바 프로그램은 기본적으로 멀티스레드 환경 |
| 73 | +- JVM이 자체적으로 여러 스레드 사용 (GC, Finalizer 등) |
| 74 | +- 프레임워크에서 생성된 스레드도 있음 → 개발자가 직접 만들지 않아도 스레드 기반 |
| 75 | + |
| 76 | +**주요 스레드 사용 환경 예시** |
| 77 | + |
| 78 | +- **Timer**: TimerTask는 Timer 내부 스레드에서 실행됨 |
| 79 | +- **Servlet/JSP**: 동시에 여러 요청 처리 → 스레드 안전하게 설계 필요 |
| 80 | +- **RMI**: 서버 측에서 스레드로 동작 → 안전성 보장 필요 |
| 81 | +- **AWT/Swing**: GUI 애플리케이션은 비동기 구조, 이벤트 스레드에 의존 |
| 82 | + |
| 83 | +## 더 알아보기 |
| 84 | + |
| 85 | +- **자바는 언어와 표준 라이브러리 차원에서 스레드 기능이 처음부터 내장된 대표적인 언어** |
| 86 | + - 비교 |
| 87 | + - C나 Python 같은 언어는 **스레드 기능이 외부 또는 표준 라이브러리를 통해 제공되는 구조** |
| 88 | + - Python은 GIL(Global Interpreter Lock)으로 인하여 CPU 병렬 처리에 한계, 스레드를 사용해도 실제로는 진짜 병렬성이 제한됨 |
| 89 | + - Go는 `goroutine`이라는 lightweight thread를 언어에 내장했지만, 이는 OS 스레드가 아닌 Go runtime이 관리하는 가상 스레드임. |
| 90 | + - 특징 |
| 91 | + - 쉬운 접근성: 개발자가 별도 라이브러리 없이 바로 스레드 생성 및 실행 가능 |
| 92 | + - 풍부한 API: `Executor`, `ThreadPool`, `Callable`, `Future`, `synchronized`, `Lock`, `Semaphore` 등 다양하게 지원 |
| 93 | + - 플랫폼 독립성: JVM이 OS에 맞춰 스레드 처리해줘서, 크로스 플랫폼에서도 동작 가능 |
| 94 | +- 동시성 문제 이슈 경험 공유 |
| 95 | + - (배경 상황) 동시성 문제를 어떻게 인지했는지 |
| 96 | + - (문제 인지) 어떻게 동시성 문제임을 확인하고자 테스트했는지 |
| 97 | + - (문제 해결) 동시성 문제를 어떻게 해결했는지 |
| 98 | + |
| 99 | +### 스터디 기록 |
| 100 | + |
| 101 | +- 멀티스레드로 인한 성능 이슈 |
| 102 | + - 책에서 소개하는 스레드 사용 시 위험성인 성능 이슈들이 크게 와닫지 않음. 이에 대해 어떻게 생각하는가. |
| 103 | + - 과거 실무 경험 스레드 1000개 사용했을 때도 큰 성능 이슈를 느끼지 못함 (일반적으로 스레드는 200개정도 사용함) |
| 104 | + - ex. 낙관적 락 vs. 비관적 락 성능 차이등을 이론 아닌 실제로 느껴보았나 |
| 105 | + - → 경진님 비교테스트 경험 소개. |
| 106 | +- 동시성 문제를 어떻게 인지하는가 |
| 107 | + - 운영 중 문제가 생기거나 로그를 기반으로 인지 가능한 경우가 많음. |
| 108 | + - 전에 인지하거나 테스트하기 어려움 |
| 109 | + - 예상 가능한 대표 사례 |
| 110 | + - 좋아요 수, 조회수 증가: 정확한 숫자를 유지해야 하는 경우. |
| 111 | + - 계좌 입출금: 금전 관련 동작은 동시성 문제 발생 시 심각한 결과로 이어질 수 있음. |
| 112 | + - 티켓 예매: 순서를 보장해야 하는 상황. |
| 113 | +- 동시성 이슈 소개: 유저 정보 업데이트 |
| 114 | + - **상황 요약** |
| 115 | + - 기존에는 이름, 이메일, 전화번호를 각각 별도의 API와 서비스 로직으로 업데이트. |
| 116 | + - 이를 하나의 통합 API로 개선하여 여러 필드를 동시에 수정할 수 있게 리팩토링 진행. |
| 117 | + - **테스트 과정에서 발생한 문제** |
| 118 | + - 테스트 시 기존 방식(여러 API 동시 호출)과 새 방식(통합 API 단일 호출)의 결과가 다르게 나옴. |
| 119 | + - 내부적으로 JPA를 사용하고 있었으며, 동시성 제어(`@Version`, 낙관적 락 등)는 적용하지 않은 상태. |
| 120 | + - **문제 원인** |
| 121 | + - 동일한 유저 데이터를 **동시에 두 스레드가 조회**하여 서로 다른 필드를 수정하고 저장. |
| 122 | + - 하지만 JPA는 **조회 시점의 전체 엔티티 상태를 기준으로** 업데이트 SQL을 생성. |
| 123 | + - 이로 인해, 먼저 저장한 스레드의 수정 내용이 나중 스레드의 `UPDATE`에서 **덮어쓰기되어 무효화**됨. |
| 124 | + - **결과** |
| 125 | + - 두 필드를 각각 수정했지만, 한쪽의 수정 내용이 사라지는 동시성 이슈 발생. |
0 commit comments