로코
December 10, 2025

Java 동시성 되짚어보기

발행일 December 10, 2025  •  8분 소요  • 1656 단어  • 다른 언어 선택:  English
Table of contents

0. 서론

부끄럽게도 이번 해에 백엔드 부트캠프 과정을 수료하면서 “동시성” 이라는 키워드를 처음 접해봤다.
사실 부트캠프를 수료하는 과정에서 다양한 Lock 방식을 실습해보면서 동시성을 제어하는 방법은 충분히 숙지할 수 있었다.
하지만 문제를 해결하는 방법에만 집중하기 보다는 “동시성"에 대해 본질적인 이해를 하고 넘어가야 앞으로도 백엔드 개발자로서의 전문적인 시야를 갖출 수 있겠다는 생각이 들었다.
따라서 동시성 프로그래밍이라는 것이 무엇인지, 또 Java 진영에서 동시성을 다루는 방법들이 어떻게 발전해왔는지를 살펴보기로 했다.

1. The Free Lunch Is Over (2004)

2004년 12월, Microsoft의 개발자였던 Herb Sutter라는 분이 The Free Lunch Is Over 라는 글을 발표했다.
이 글은 아래의 강렬한 표어와 함께 시작된다.

The biggest sea change in software development since the OO revolution is knocking at the door, and its name is Concurrency.

객체 지향 혁명 이후 가장 큰 변화의 물결이 소프트웨어 개발의 문을 두드리고 있다. 그것은 바로 동시성이다.

Herb Sutter는 1965년에 “반도체에 집적되는 트랜지스터의 수가 매년 2배씩 증가한다"라고 주장했던 무어의 법칙이 점차 한계에 봉착하고 있다고 설명한다.
2001년 8월에 인텔 칩의 CPU 클럭 속도는 2GHz였는데, 이 글이 발표되었던 2004년 12월에도 4GHz의 속도를 갖춘 CPU가 등장하지 않았다고 말해준다.
사실 4GHz의 클럭 속도는 머지않아 달성할 수 있어 보이지만, 과연 10GHz의 클럭 속도를 달성할 수 있을까?
아쉽게도 제한된 반도체 용적 안에 과도하게 많은 트랜지스터를 집적하게 되면 발열, 전력 소모, 전류 누설의 문제가 발생하기 때문에 더 높은 클럭 속도를 달성하기가 점점 어려워진다.
그렇기 때문에 심지어 2025년 현재에도 고성능 CPU가 낼 수 있는 최대 클럭 속도는 6GHz 정도의 수준에 머물러 있다.

이에 Herb Sutter는 개발자가 매년 컴퓨터 업그레이드 요청만 하면 자동으로 성능을 끌어올릴 수 있었던 ‘공짜 점심’ 이 이미 1~2년 전에 끝났으며, 현대 애플리케이션의 기하급수적으로 높아지는 CPU 처리량에 대비하기 위해 동시성 프로그래밍이 점점 더 중요해질 것이라고 강조한다.
또한 Java와 같은 프로그래밍 언어 차원에서도 동시성 프로그래밍 모델이 절실히 필요하다고 언급한다.

2. JSR-133/166 (2004)

이러한 흐름에 발 맞춰서 JCP(Java Community Process)는 2004년에 JSR(Java Specification Request) 133 및 166을 승인한다.
위 2개 스펙을 통해 Java 언어는 동시성 프로그래밍을 수행할 수 있는 언어로 확고하게 자리 잡았다.
어떠한 변경사항들이 있었는지 간결하게 살펴보자.

JSR-133

사실 Java 언어는 1.0 때부터 JMM(Java Memory Model)을 통해 멀티스레드를 다룰 수 있었다.
하지만 final 필드의 값을 변경할 수 있다거나, 동기화를 위한 재정렬을 허용하지 않는 등 심각한 결함들이 존재했다.

재정렬: 컴파일러, JIT, 캐시 등 다양한 요인에 따라 연산의 순서는 코드 상에서의 흐름과 달라질 수 있는데, 동기화 작업을 수행하려면 컴파일러, 런타임, 하드웨어는 마치 직렬 실행이 되는 것처럼 공모(협력)해야 하며, 이 과정을 ‘재정렬’이라고 부름

그렇기 때문에 JSR-133은 JMM의 결함들을 개선하고, volatile, synchronized, final 키워드들이 직관적으로 작동하는 것을 핵심 목표로 삼고 진행되었다.
개발자가 멀티스레드 프로그램이 어떻게 메모리와 상호작용하는지 자신 있게 추론할 수 있도록 도우며, 다양한 유명 아키텍처들을 정확성과 고성능을 두루 갖춘 채 구현할 수 있도록 제공하고자 했다.
개략적인 개선 사항들은 다음과 같다.

JSR-166

JSR-166에서는 스펙 리드였던 Doug Lea의 주도하에, 동시성을 다루는 패러다임을 ‘언어 지원 방식’ 에서 ‘라이브러리 지원 방식’ 으로 전환했다.
JSR-133에서는 low-level의 언어 차원에서 메모리 모델을 제공했다면, JSR-166에서는 high-level에서 추상화 도구를 제공한 것이다.
추상화 도구는 java.util.concurrent 패키지와 함께 제공되었으며, 핵심적인 구성요소는 다음과 같다.

JSR-166에서 전하는 메시지는 명확하다.
바로 “스레드를 직접 다루지 말고 고수준으로 추상화한 동시성 라이브러리를 활용하자” 라는 것.

3. Akka / RxJava

Java 언어 차원에서 스레드 추상화 기법이 제공되었지만, 이 방식에는 고질적인 문제가 있다.
바로 Lock 기반 프로그래밍의 특성상 Race Condition이 발생할 수밖에 없고, 그렇기 때문에 높은 차원의 동시성 환경에서 확장성의 한계가 존재한다는 점이다.
이로 인해 2000년대 후반 JVM 생태계에서는 “공유하지 않으면 동기화할 필요 없다” 라는 원칙 기반의 패러다임들이 생겨났다.

Akka (2009)

Akka는 Actor 모델을 기반으로 작동한다.
1973년, Carl Hewitt가 MIT에서 Actor 모델 이론을 발표했고, 이것을 기반으로 1986년에 구현된 언어가 Erlang이다.
그리고 이러한 Actor 모델의 철학을 Jonas Bonér가 Scala 및 JVM 프레임워크로 구현했는데, 이것이 바로 Akka 이다.

Actor 모델은 Lock을 사용하지 않는 대신 캡슐화를 강제한다.
신호에 반응하는 협력 엔티티들로 구성되어 있으며, 서로에게 신호를 보냄으로써 전체 애플리케이션이 작동한다.
이는 실제로 세계가 소통하는 방식과도 유사한 동작 방식이라고 할 수 있다.
Akka의 대략적인 개념들은 다음과 같다.

RxJava (2013)

2009년, Microsoft에서 Rx.NET을 개발함으로써 Reactive Programming 패턴을 정립했다.
이를 기반으로 2013년에 Netflix는 RxJava를 개발해서 ReactiveX 패턴을 JVM으로 이식했다.
RxJava 코드의 형태는 다음과 같다.

Observable<String> videos = getVideos()
    .flatMap(video -> getMetadata(video))
    .filter(metadata -> metadata.rating > 4.0)
    .map(metadata -> metadata.title)
    .timeout(1, TimeUnit.SECONDS);

RxJava를 활용하면 서비스에 대한 호출을 병렬로 실행하고 결과를 조합하는 방식을 쓸 수 있게 된다.
RxJava에 대해서도 핵심적인 개념들을 정리해봤다.

4. Reactive Manifesto (2013)

동시성 혁명이 시작된 이후로 위와 같이 다양한 접근 방식들이 난립하게 되었다.
Akka를 창시했던 Jonas Bonér는 저마다 다른 해결책들 사이에서 공통점을 발견했고, 곧이어 동료들과 함께 원칙들을 정리하고 발표하기에 이른다.

Reactive Manifesto

Reactive 선언문은 4가지의 핵심 원칙을 담고 있다.

Responsive (응답성)

Resilient (탄력성)

Elastic (유연성)

Message Driven (메시지 구동)

5. Spring WebFlux (2017)

Spring Framework 5.0과 함께 Spring WebFlux가 출시되었다.
이는 기존 Servlet 기반의 Spring Web MVC와는 차별점을 가지는, Reactive Stack Web Framework이며, 아래와 같은 특성들을 가진다.

Spring WebFlux는 non-blocking I/O 기반 비동기 처리와 함께 빠른 응답성(Responsive) 을 보장하고, Mono/Flux를 활용해 비동기 데이터 스트림(Message Driven) 을 활용하므로 Reactive 프로그래밍 프레임워크라고 볼 수 있다.
이로써 Java 진영에서 동시성을 다루는 방식이 프레임워크 단계에서는 정립이 되었다고 보인다.

6. Project Loom (2023)

Java 21의 Project Loom이 해결하고자 했던 문제점들은 다음과 같다.

실제로 Netflix 개발자는 아래와 같은 말을 남겼다고 한다.

We spent more time debugging reactive chains than we saved in scalability.

우리는 확장성 측면에서 절약한 시간보다 더 많은 시간을 반응형 체인 디버깅에 썼다.

Project Loom은 이러한 문제점들을 제거하면서 혁신적인 기능들을 제공해준다.

Project Loom은 동시성 관리를 기존의 OS 커널 중심에서 JVM 중심으로 옮겼고, 이를 통해 스케줄링, 메모리 관리, 관측을 개발자가 완벽하게 제어할 수 있게 해주었다.
Reactive 방식에 대한 러닝 커브를 줄일 수 있는 대안책을 마련해주었으며, Debuggability도 확보할 수 있도록 해주었다.

7. 결론

The Free Lunch Is Over에서는 동시성과 관련된 문제를 인식했고, JSR-133/166은 언어 차원에서의 해결책을 제시했다.
Akka/RxJava는 Java 진영에서 동시성을 다루기 위한 실험적인 접근을 했으며, Reactive Manifesto를 통해 마침내 Reactive 철학이 정립되었다.
Spring WebFlux는 Reactive를 프레임워크에 녹여내었는데, Java는 Project Loom을 통해 동시성 관련 문제를 근본적으로 재설계했다.
The Free Lunch Is Over 발표 후 20여 년이 흘렀지만 “동시성 혁명"은 아직도 끝나지 않은 듯 하다.
하지만 이번 기회에 동시성을 다루는 패러다임이 변화되는 과정을 지켜보면서 어떤 핵심 가치들이 발생했는지 알 수 있게 되었으며, 앞으로도 동시성 프로그래밍을 다룰 때 어떤 방식으로 접근하면 될지에 대한 시야와 관점을 얻어갈 수 있었다.

References

Contact me

email: nmin1124@gmail.com