자바의 동시성 #1 - 물리적 아키텍쳐와 자바 스레드

Jan 14, 2019


독자들이 필자의 Effective Java 3rd의 동시성에 관한 포스팅을 읽거나 동시성에 대해 공부할 때, 동시성에 관한 기본 개념을 알아두면 좋겠다고 생각되어 이번 포스팅을 작성하게 되었다. 사실 동시성에 관한 라이브러리들이 발전하면서 자바가 제공하는 low level 수준인 동시성 키워드(synchrozied, volatile)와 동시성 메소드(wait, notify)를 직접 사용할 일이 거의 없을 것이다. 하지만 잘 만들어진 라이브러리를 잘 사용하려면 내부에서 어떻게 동작하는지 머릿속으로 상상할 수 있어야 하고, 라이브러리가 제공하지 않는 기능을 구현하기 위해서는 자바가 제공하는 low level 수준의 동시성 개념을 잘 알 필요성이 있다. 이를 쉽게 이해하기 위해 자바 스레드가 하드웨어 수준에서 어떻게 연관되어 동작하는지 살펴볼 것이다.

물리적 아키텍쳐에서의 하드웨어적 스레드

요즘 대부분의 컴퓨터는 CPU가 멀티코어를 지원한다. n코어 m스레드라고 가정한다면, n은 말 그대로 “물리적인” CPU 코어 유닛 개수이다. 명령어를 메모리에서 뽑아 해석하고 실행하는 반도체 유닛이 n개가 있는것이다. CPU 한 개는 여러개의 코어를 가질수 있다. m은 “논리적” 코어 개수이며, “동시에 실행가능한 스레드 개수”이다. n=m인 경우가 가장 직관적인데 가끔 m이 n보다 크기도 하다. 이 경우 물리적 코어 하나가 스레드 두 개 이상을 동시에 실행 가능하다는 의미가 된다. 같은 말로, 동시에 두 개 이상의 작업을 병렬 수행 가능하다는 의미가 된다. 운영체제가 스케줄링을 할 때, “동시에 실행 가능한 스레드 수”, 즉 할당 가능한 스레드의 슬롯이 m이다. 예를 들어, 다음 그림은 CPU가 2개인 4코어 8스레드를 의미한다. 8개의 작업을 동시에 처리할 수 있음을 의미한다.
hardware-thread-01

그럼 여기서 말하는 스레드는 자바의 스레드와 같은 것인가?? 아니다. 방금까지 언급됐었던 스레드는 하드웨어적 스레드이다. 그럼 자바의 스레드란 무엇이란 말인가??

물리적 아키텍쳐에서의 소프트웨어적(자바) 스레드

1코어 1스레드 CPU 쓰던 시절에도 자바 스레드는 수십 개씩 띄우는 게 가능했었다. 하드웨어적 스레드가 1개인데 자바 스레드를 수십 개 띄우는 것이 가능하다니, 무언가 말이 이상하지 않은가? 자바 스레드가 무엇인지를 알기 전에, 여기서 잠깐 병렬성(Parallelism)동시성(Concurrency)이라는 개념을 집고 가자. 병렬성은 작업들이 병렬로 실행되는 성질을 의미하는 것이다. 위에서 언급한 하드웨어적 스레드는 병렬성을 가지고 있다. 동시성은 여러개의 작업들이 짧은 시간내에 번갈아 가면서 병렬로 처리되는 것처럼 보이도록 실행되는 성질을 의미하는 것이다. 자바 스레드는 동시성을 가지고 있기 때문에 하드웨어적 스레드 개수보다 더 많은 스레드를 생성하여 하드웨어적 스레드를 번갈아 가면서 사용하여 작업을 처리한다. 예를 들어, 다음 그림은 N개의 자바 스레드가 CPU가 2개인 4코어 8스레드를 번갈아 가면서 사용함을 의미한다.
software-thread-01

위의 그림에서 어느 순간에 병렬로 실행되는 자바 스레드의 개수는 최대 8개이지만, N개의 모든 자바 스레드가 번갈아 가면서 실행되므로 병렬로 실행되고 있는 것처럼 보일것이다. 이와 같은 스레드를 소프트웨어적 스레드라고 한다. 자바 스레드(소프트웨어적 스레드)는 동시성의 성질을 가지고 있기 때문에, 자바에서는 멀티 스레드와 관련된 프로그래밍을 병렬성 프로그래밍이라 하지 않고 동시성 프로그래밍이라고 한다.

동시성을 지원하면 컨텍스트 스위칭 등 추가적인 비용이 발생함에도 불구하고, 자바는 왜 소프트웨어적 스레드를 채택했을까? 위의 예제에서 사용한 CPU가 2개인 4코어 8스레드를 예로 들어보자. 현재 총 16개의 작업이 있다고 가정을 해보자. 8개는 오래 걸리는 작업, 나머지 8개는 짧은 시간을 필요로 하는 작업이다. 최악의 경우 8개의 오래 걸리는 작업이 동시에 처리될 수도 있는데, 이 경우 나머지 8개의 작업은 처리하는데 짧은 시간이 걸리는 데에도 불구하고 현재 처리중인 8개의 작업이 다 끝날때 까지 기다려야 한다. 만약 짧은 시간을 필요로 하는 작업을 처리한 결과가 필요한 또 다른 작업이 있다면, 이 작업들은 8개의 오래 걸리는 작업 때문에 많은 시간을 기다려야 한다. 이를 방지하기 위해 작업을 번갈아 가면서 처리하는 소프트웨어적 스레드를 채택했을 것이다.

다음 포스팅에서는 동시성 프로그래밍에서 발생할 수 있는 문제점과 자바에서는 이를 어떻게 해결할 수 있는지 살펴보도록 하겠다.


참고한 리소스 : https://kldp.org/node/154708