본문 바로가기

Java

Java Thread와 Concurrency(동시성) 이해하기

동시성에 관한 Library들이 계속 발전하면서 Java가 제공하는 Low level 수준의 동시성 키워드인 synchronized, voliatile과 동시성 메서드인 wait, notify는 개발자가 직접 사용할 일이 거의 없어졌다. 하지만 Library를 사용하여 Software를 개발하는 경우 Library를 의도대로 잘 사용하기 위해 내부에서 어떻게 동작이 되는지 잘 이해 할 수 있어야 하고, Library가 제공하지 않는 기능을 구현하기 위해서는 Low level 수준의 동시성 개념을 잘 알아두어야 할 필요성이 있다. Java에서의 동시성 개념을 이해하기 위해 Java Thread가 Hardware Thread와 어떻게 연관되어 동작하는지 알아보겠다.

Hardware Thread

요즘 대부분의 많은 컴퓨터는 Multi-core CPU가 사용된다. CPU는 여러 개의 Core를 가질 수 있다.

N Core, M Thread의 경우 N은 물리적인 CPU Core unit 개수이고 이 Core unit 개수는 명령어를 메모리에서 가져와 해석 후 실행하는 반도체 unit N개를 의미한다. M Thread는 논리적 Core이며, 동시에 실행 가능한 Thread 개수이다.

M이 N보다 큰 경우 물리적 Core 하나가 Thread 두 개 이상을 동시에 실행 가능하다는 것을 의미한다. 즉 동시에 두 개 이상의 작업을 병렬로 수행할 수 있음을 의미하고, OS가 Scheduling을 진행할 때 동시에 M만큼 실행 가능한 Thread 슬롯을 할당받아 사용하는 것을 의미한다.

위에서 이야기한 Thread는 Hardware Thread이므로 Java의 Thread와 다르다. Java의 Thread는 무엇일까?

Software Thread

Hardware 성능이 그다지 좋지 못하던 시절에 단일 Core, 단일 Thread CPU로도 Java의 Thread는 여러 개를 사용할 수 있었다. Hardware에 Thread가 한 개인데, Java Thread는 여러 개를 사용할 수 있다니 무슨 말인가? 이것을 이해하기 위해서는 Parallelism(병렬성)과 Concurrency(동시성)의 개념과 차이를 알아야 한다. Parallelism은 Hardware Thread에서 설명한 것처럼 여러 작업이 병렬로 실행되는 성질을 의미하고, Concurrency는 여러 작업이 병렬로 실행되는 것처럼 보이지만 사실은 여러 작업이 찰나의 시간에 계속 번갈아 가며 실행되는 것을 의미한다. (두 Thread가 교차하며 실행되는 과정은 Context Switching(문맥 교환)이라 부른다.)

 

Parallelism

 

Concurrency

 

위 그림에서 Hardware Thread가 총 8개라 가정한다면, 어느 특정 순간에 병렬로 실행될 수 있는 Java Thread 개수는 최대 8개가 될 수 있지만, N개의 모든 Java Thread는 모두 서로 교차하며 실행되므로 병렬로 실행되는 것처럼 보인다. 이러한 것을 Software Thread라고 부르고 Java Thread는 동시성의 성질을 가지고 있기 때문에, Multi-Thread와 관련된 Programming을 Parallelism Programing이라 부르지 않고, Concurrency Programming이라 부른다.

 

왜 Java는 Software Thread를 채택했을까?

Concurrency를 지원하려면 여러 작업이 Thread간에 교차하며 실행될 때 Context Switching이라는 추가적인 비용이 발생하는데 Java는 왜 Software Thread를 사용하는가? 그 이유는 Hardware Thread를 그대로 사용할 경우 OS Scheduling에 의존적으로 JVM이 구동되어서 그렇지 않을까 생각한다. Hardware Thread가 8개 있는 상황에서 현재 총 10개의 작업이 존재하는데 그중에 8개는 오래 걸리는 작업, 나머지 2개는 짧은 시간을 필요로 하는 작업이 있다. OS Scheduling에 따라서 현재 처리 중인 오래 걸리는 작업 8개가 Hardware Thread를 점유하고 있는 경우 나머지 두 작업은 짧은 시간안에 처리될 수 있음에도 불구하고 처리 중인 작업 8개가 끝나기를 오랫동안 기다려야 하는 경우가 발생할 수 있다. Java는 이를 방지하기 위해 여러 작업을 번갈아 실행할 수 있는 방식으로 Software Thread를 사용하는 것이 아닌가 싶다.