동시성 프로그래밍 관점에서 Golang vs Java
Golang과 Java 각각의 프로그래밍 언어를 동시성 프로그래밍의 관점에서 차이점을 정리해본다.
Golang 을 처음 공부 하다 보면 '고루틴(goroutine)' 이라는 용어를 많이 접할 수 있다. 이 고루틴은 쓰레드 처럼 동시성 처리를 위해 사용된다. 그러면 이 고루틴이 다른 언어들의 동시성 처리 방법과 어떤점이 다른지, 그 중에 Java 와 비교해서 차이점을 알아본다.
우선 기존의 Java 에서 사용하는 쓰레드(Thread) 란?
말 그대로 우리가 기존에 알고 있던 프로세스와 쓰레드 개념에서 나온 쓰레드 라고 생각하면 된다. 하나의 프로세스 안에 여러 쓰레드가 존재할 수 있으며, 이 쓰레드를 이용해 여러 작업을 동시에 처리 하고, 각 쓰레드 간의 데이터 교환도 가능하다. Java 에서는 이런 쓰레드를 Jvm 위에서 관리될 수 있도록 도와준다.
그러면 Golang 에서 사용하는 고루틴이란?
고루틴은, 쓰레드와 비슷하게 동시성 작업을 위한 목적으로 존재하지만, 실행되는 단위에서 차이가 있다. 위에서 설명 한 것 처럼 Java 는 쓰레드와 OS 커널에서의 쓰레드가 1:1 로 매핑 되는 것에 반면에, Golang 에서는 고루틴과 OS 커널 쓰레드가 N:M 로 매핑이 된다. 즉, 여러개의 쓰레드 위에 여러 개의 고루틴을 돌릴 수 있다.
이렇게 되면 Java 에서 동시성 처리를 위해 쓰레드를 사용하는 것보다 훨씬 더 많은 양의 동시성 처리 루틴을 생성하고 실행할 수 있게 된다. 그래서 고루틴을 경량 쓰레드 라고 부르기도 한다.
고루틴이 쓰레드 위에서 경량화 되어서 동작하는 방법
그러면 실제로, 고루틴이 쓰레드 보다 경량화 되어 있다고 볼 수 있을까?
고루틴이 동작하는 방식은 간단하게 보면, 여러개의 쓰레드가 있고, 그 위에서 경량화된 동시성 객체들이 존재한다고 볼 수 있다. 그리고 이러한 객체들을 고루틴에서 관리하고 있어서 하나의 쓰레드에 여러개의 고루틴이 동작할 수 있게 된다. 각 고루틴은 스택의 2KB 정도만을 할당 받고, 고루틴 사용량에 따라 동적으로 증가 되기 때문에 기본적으로 MB 단위의 쓰레드보다 메모리 효율성이 좋다.
Golang 에서 많은 양의 고루틴을 띄워도 괜찮을까? 얼마나 띄울수 있을까?
Golang 문서에서 보면, 수십만개의 고루틴을 동일한 주소 공간에 생성할 수 있다고 나와 있다. 예를 들면, 약 4GB 메모리를 가진 머신 위에서 100만개의 고루틴을 동작시킬 수 있는 것이다. 고루틴의 동작 특성에 따라 차이는 있겠지만.. 사실상 프로그램을 실행 하며 몇만개의 동시성 요소를 실행하는 일은 흔치 않기 때문에 사용에 제한이 없다고 봐도 될것 같다.
Reference
[1] https://www.baeldung.com/cs/process-vs-thread
[2] https://medium.com/@genchilu/javas-thread-model-and-golang-goroutine-f1325ca2df0c