자바 프로그래밍에서는 멀티 스레드를 쉽게 구현할 수 있다는 장점이 있다고 한다. 이 내용을 이해하기 전에 프로세스와 스레드에 대한 개념을 잡아보고자 한다.
우선 프로세스와 스레드에 대한 간략한 설명부터 확인해보자.
프로세스는 운영체제로부터 자원을 할당받은 작업의 단위이며, 스레드는 프로세스가 할당받은 자원을 이용하는 실행 흐름의 단위를 의미한다.
프로그램은 파일이 저장 장치에 저장되어 있지만 메모리에는 올라가 있지 않은 정적인 상태를 의미한다.
"파일이 저장 장치에 저장되어 있지만 메모리에는 올라가 있지 않은" 을 해석해보면 운영체제가 프로그램에게 독립적인 메모리 공간을 할당해주지 않았다는 뜻이다. 모든 프로그램은 운영체제가 실행되기 위한 메모리 공간을 할당해주어야 실행될 수 있다.
"정적인 상태" 는 말 그대로 움직이지 않는 상태를 의미한다. 즉, 아직 실행되지 않고 가만히 있다는 의미다.
결국 프로그램은 아직 실행되지 않은 파일(.exe, .dmg)을 의미하는 코드 덩어리다.
프로그램을 실행하는 순간 파일은 컴퓨터의 메모리에 올라간다. 그리고 프로그램이 동적인 상태로 변하는데, 이를 프로세스라고 한다.
과거에는 프로그램은 하나의 프로세스만을 사용했었다. 하지만 시간이 지나면서 프로그램이 복잡해지고 프로세스 하나만으로 프로그램을 실행하기에는 어려워지게 되었다. (실제로 프로그램 하나가 단순히 한 가지 작업만을 하는 경우는 없다. 🤔)
그러면 하나의 프로그램에 여러 개의 프로세스를 만들면 되지 않을까? 라는 생각을 할 수 있지만 불가능한 일이었다. 그 이유는 운영체제에서 안정성을 위해 프로세스마다 자신에게 할당된 메모리 내의 정보에만 접근할 수 있도록 제약을 두었기에 이를 벗어나는 정보에 접근하려면 오류가 발생하게 되기 때문이다.
이 때 등장한 개념이 스레드다. 스레드는 프로세스와는 다른 더 작은 실행 단위의 개념이며, 스레드는 프로세스와 달리 스레드 간 메모리를 공유하며 작동한다.
자바의 스레드는 일반 스레드와 거의 차이가 없고 JVM이 운영체제의 역할을 수행해준다. 자바에는 프로세스가 존재하지 않고 스레드만 존재하며, 자바 스레드는 JVM에 의해 스케줄되는 실행 단위 코드 블록이다.
프로세스는 메모리에 올라갈 때 운영체제로부터 시스템 자원을 할당을 받는다. 운영체제는 프로세스마다 각각 독립된 메모리 영역을 Code/Data/Stack/Heap 형식으로 할당해준다. 각 독립된 메모리 영역을 할당해주기 때문에 프로세스는 다른 프로세스의 변수나 자료에 접근할 수 없다.
(프로세스간 정보 공유가 불가능한것은 아니다. IPC/LPC를 사용하거나 별도의 공유 메모리를 만들어서 정보를 주고 받도록 설정하여 프로세스간 정보를 공유할 수 있긴 하지만 자원 부담이 크다는 정도만 알아두자!)
반면 스레드는 메모리를 서로 공유할 수 있다. 프로세스가 할당받은 메모리 영역 내에서 Stack 형식으로 할당된 메모리 영역은 따로 할당받고, 나머지 Code/Data/Heap 형식으로 할당된 메모리 영역을 공유한다. 즉, 모든 프로세스에는 하나 이상의 스레드가 존재한다.
프로세스의 경우 실행하다 오류가 발생해서 강제 종료가 되면, 공유하고 있는 파일을 손상시키는 경우가 아니라면 다른 프로세스에 영향을 주지 않는다. 하지만 스레드는 Code/Data/Heap 메모리 영역을 공유하기 때문에 하나의 스레드에서 문제가 발생하면 같은 프로세스 내의 다른 스레드 모두 강제로 종료된다.
CPU는 작업을 처리할 때 스레드를 최소 단위로 삼고 작업을 한다. 그에 비해 운영체제는 작은 단위까지 직접 작업하지 않기 때문에 프로세스가 최소 작업 단위가 된다.
프로세스와 스레드를 설명할 때 Code/Data/Stack/Heap에 대해 언급을 했었는데, 이에 대한 내용도 함께 살펴보자.
프로그램이 실행되기 위해서는 프로그램이 메모리에 로드되어야 한다. 그리고 프로그램에서 사용되는 변수들을 저장할 메모리도 필요하다. 따라서 운영체제는 프로그램의 실행을 위해 다양한 메모리 공간을 제공하는데, 대표적으로 Code, Data, Stack, Heap 메모리 공간이 있다.
출처: http://www.tcpschool.com/c/c_memory_structure
코드 영역은 실행할 프로그램의 코드가 저장되는 영역으로 텍스트 영역이라고도 한다. CPU는 코드 영역에 저장된 명령어를 하나씩 가져가서 처리하게 된다.
데이터 영역은 프로그램의 전역 변수와 정적 변수가 저장되는 영역이다. 데이터 영역은 프로그램의 시작과 함께 할당되며 프로그램이 종료되면 소멸한다.
스택 영역은 함수의 호출과 관계되는 지역 변수와 매개 변수가 저장되는 영역이다. 스택 영역은 함수의 호출과 함께 할당되며, 함수의 호출이 완료되면 소멸한다.
스택 영역은 Push 동작으로 데이터를 저장하고 Pop 동작으로 데이터를 뽑아낸다. LIFO 방식으로 동작하기 때문에 가장 늦게 저장된 데이터가 가장 먼저 인출되는 형태이다. 그리고 스택 영역은 메모리의 높은 주소에서 낮은 주소의 방향으로 할당된다.
힙 영역은 사용자가 직접 관리할 수 있는 그리고 해야만 하는 메모리 영역이다. 힙 영역은 사용자에 의해 메모리 공간이 동적으로 할당되고 해제된다. 힙 영역은 메모리의 낮은 주소에서 높은 주소의 방향으로 할당된다.