반응형

Backend Core/Java Concurrency 9

Java Concurrency 정리 - 무엇을 믿고, 무엇을 의심해야 하는가?

지금까지 Java의 동시성 도구들을 하나씩 살펴봤다.volatile, synchronized, Lock, Atomic, Thread Pool, CompletableFuture, Concurrent Collections까지.이제 중요한 질문은 이것이다. 이 도구들을 어디까지 믿어도 되는가? 그리고 무엇은 반드시 의심해야 하는가? 이 글에서는 Java Concurrency를 기능 나열이 아니라 판단 기준으로 정리한다.1. Java가 보장하는 것의 경계Java는 동시성 문제를 해결해주지 않는다.Java가 제공하는 것은명확한 규칙최소한의 보장뿐이다.즉, “이 규칙을 지키면 이 정도까지는 보장한다” 라는 계약을 제공할 뿐이다.그 계약을 벗어난 동작에 대해서는 아무런 약속도 하지 않는다.2. 믿어도 되는 것다음은 ..

Concurrent Collections - 락은 어디까지 분해되었는가?

멀티스레드 환경에서 공유 컬렉션은 동시성 문제의 단골 원인이다.단순히 “락으로 감싸면 안전하다” 는 접근은 성능과 확장성 측면에서 곧 한계에 부딪힌다.이 글에서는 Java의 Concurrent Collections가 어떤 설계 철학으로 만들어졌는지, 그리고 락을 어디까지 분해했는지를 공식 문서 기준으로 정리한다.1. 왜 일반 컬렉션은 위험한가?HashMap, ArrayList 같은 일반 컬렉션은 스레드 안전하지 않다.여러 스레드가 동시에 접근하면데이터 손상무한 루프예측 불가능한 결과가 발생할 수 있다.이 문제를 피하기 위해 synchronized로 전체를 감싸는 방법이 가장 먼저 떠오른다.하지만 이는 모든 접근을 직렬화하며, 동시성의 장점을 스스로 포기하는 선택이다.2. synchronized 컬렉션의 ..

CompletableFuture - 비동기와 병렬은 다르다

Thread Pool을 사용하다 보면 자연스럽게 이런 요구가 생긴다. “이 작업이 끝난 뒤, 다음 작업을 이어서 실행하고 싶다” Java는 이를 위해 CompletableFuture를 제공한다.하지만 CompletableFuture는 종종 “비동기 마법 도구”처럼 사용되고, 그 결과는 예상과 다른 성능 저하나 복잡한 동시성 버그로 이어진다.이 글에서는 CompletableFuture가 무엇을 보장하는지, 그리고 무엇을 보장하지 않는지를 공식 문서 기준으로 정리한다.1. 비동기와 병렬은 같은 개념이 아니다CompletableFuture를 이해하기 전에 반드시 구분해야 할 개념이 있다.비동기(Asynchronous)병렬(Parallel)비동기는 “작업이 끝날 때까지 기다리지 않는다” 는 의미다.병렬은 “여러..

Thread Pool은 왜 문제를 만드는가? - ExecutorService의 책임

멀티스레드 환경에서 스레드를 직접 생성하고 관리하는 것은 거의 항상 잘못된 선택이다.그래서 Java는 Thread Pool과 ExecutorService를 제공한다.하지만 아이러니하게도, 실무에서 발생하는 많은 동시성 장애의 원인은 바로 이 Thread Pool이다.이 글에서는 Thread Pool이 왜 필요한지부터, 왜 문제가 되기 쉬운지, 그리고 ExecutorService가 무엇을 보장하고 무엇을 보장하지 않는지를 공식 문서 기준으로 정리한다.1. 왜 Thread Pool이 필요한가?Thread를 요청마다 생성하는 모델은 다음과 같은 문제를 가진다.Thread 생성 비용이 크다동시에 생성 가능한 Thread 수에는 한계가 있다OS 자원을 쉽게 고갈시킨다Thread Pool의 목적은 단순하다. 미리 ..

Atomic 클래스와 CAS - 락 없는 동시성은 어디까지 가능한가?

앞선 편에서 살펴본 synchronized와 Lock은 배타성을 통해 동시성 문제를 해결했다.하지만 배타성에는 항상 비용이 따른다.락 경합컨텍스트 스위칭대기 시간 증가이 비용을 줄이기 위한 시도로 등장한 것이 Atomic 클래스와 그 기반이 되는 CAS(Compare-And-Swap)다.이 글에서는 락 없는 동시성이 무엇을 의미하는지, 그리고 Atomic 클래스가 어디까지 안전한지를 공식 문서 기준으로 정리한다.1. Atomic 클래스는 왜 등장했는가?멀티스레드 환경에서 다음과 같은 연산은 매우 흔하다.count++;하지만 이 연산은읽기계산쓰기의 세 단계로 이루어지며, 원자적이지 않다.synchronized로 보호하면 안전하지만, 매번 락을 거는 것은 비용이 크다.Atomic 클래스는 “단일 변수에 대한..

synchronized와 Lock - 배타성의 범위는 어디까지인가?

volatile이 가시성을 보장하는 도구라면, synchronized와 Lock은 배타적 실행을 보장하는 도구다.멀티스레드 환경에서 “동시에 실행되면 안 되는 코드”를 어디까지, 어떻게 보호할 것인가는 동시성 설계의 핵심이다.이 글에서는 synchronized와 Lock이 무엇을 보장하고, 어디까지 책임지는지를 Java Memory Model과 공식 API 기준으로 정리한다.1. 배타성(Exclusivity)이 필요한 이유여러 스레드가 같은 데이터를 동시에 수정하면, 다음과 같은 문제가 발생한다.중간 상태가 노출된다불변식(invariant)이 깨진다연산 결과가 예측 불가능해진다이 문제를 해결하는 가장 직접적인 방법은 “한 번에 하나의 스레드만 실행하게 만드는 것” 이다.synchronized와 Lock은..

volatile은 무엇을 보장하고, 무엇을 보장하지 않는가?

volatile은 Java 동시성에서 가장 많이 오해되는 키워드 중 하나다.“volatile을 쓰면 스레드 안전해진다”, “락 없이도 안전하다”는 설명은 부분적으로만 맞고, 대부분의 경우는 틀리다.이 글에서는 volatile이 정확히 무엇을 보장하는지, 그리고 무엇을 절대 보장하지 않는지를 Java Memory Model 기준으로 정리한다.1. volatile은 왜 존재하는가?volatile의 존재 이유는 단순하다. 한 스레드에서 변경한 값을 다른 스레드에서 반드시 보이게 만들기 위함 즉, volatile은 가시성(visibility) 문제를 해결하기 위한 도구다.동기화 없이 공유되는 일반 변수는, 다음이 보장되지 않는다.언제 값이 메모리에 반영되는가다른 스레드에서 그 값을 언제 읽는가volatile은 ..

Java Memory Model - happens-before의 정확한 의미

Java 동시성을 이해하는 데 있어 가장 중요한 개념은 happens-before다.하지만 happens-before는 “먼저 실행된다”거나 “순서가 보장된다”는 말로 자주 오해된다.이 글에서는 happens-before가 무엇을 보장하고, 무엇을 보장하지 않는지를 Java Language Specification 기준으로 정리한다.1. 왜 happens-before가 필요한가?멀티스레드 환경에서 다음 질문은 항상 따라온다.한 스레드에서 쓴 값은 언제 다른 스레드에서 보이는가?코드에 작성한 순서가 실제 실행 순서와 같은가?직관적으로는 “먼저 실행했으니, 먼저 보일 것” 처럼 느껴진다.하지만 실제로는CPU 캐시컴파일러 최적화JIT 재정렬때문에 이 직관은 쉽게 깨진다.happens-before는 이 혼란을 ..

Java Concurrency는 무엇을 보장하는가?

Java에서 동시성 문제는 종종 “멀티스레드가 어려워서” 발생한다고 설명된다.하지만 문제의 본질은 복잡한 문법이나 API가 아니라, 무엇이 보장되고 무엇이 보장되지 않는지를 명확히 이해하지 못한 상태에서 코드를 작성하는 데 있다.이 글에서는 Java가 동시성 환경에서 무엇을 보장하는지, 그리고 무엇을 의도적으로 보장하지 않는지를 공식 문서 기준으로 정리한다.1. 동시성 문제는 어디서 발생하는가?동시성 문제는 여러 스레드가 같은 데이터에 동시에 접근할 때 발생한다.문제는 단순히 “동시에 실행된다”는 사실이 아니라,어떤 값이 언제 보이는가실행 순서가 어떻게 재배치되는가한 스레드의 결과를 다른 스레드가 신뢰할 수 있는가를 알 수 없다는 점이다.이 불확실성은CPU 캐시컴파일러 최적화JIT재정렬(Reorderin..

반응형