이번에는 쓰레드의 메모리 구성에 대해서 설명해보겠다. 쓰레드가 생성되면 가상머신은 쓰레드의 실행을 위한 별도의 메모리 공간을 할당한다고 설명하였다. 그렇다면 이러한 별도의 메모리 공간은 정확히 무엇을 의미하는 것일까?
쓰레드의 가장 큰 역할은 별도의 실행흐름 형성이다. 그리고 별도의 실행흐름은 메소드의 호출을 통해서 형성된다. 즉 처음에는 run 메소드가 호출되고, run 메소드 내에서는 또 다른 메소드를 호출하면서 main 메소드와는 다른 흐름을 형성한다. 이렇듯 main 메소드와는 전혀 다른 실행흐름을 형성하기 위해서는 별도의 스택이 쓰레드에게 할당되어야 한다. 따라서 main 쓰레드 이외의 두 개의 쓰레드가 추가로 생성되면, 가상머신은 다음의 형태로 메모리를 구성한다.
위 그림에서 보듯이 모든 쓰레드는 자신의 스택을 할당 받는다. 그러나 힙과 메소드 영역은 모든 쓰레드가 공유한다. 여기서 특히 힙이 공유됨에 주목하자. 힙 영역이 공유된다는 것은 모든 쓰레드가 동일한 힙 영역에 접근이 가능함을 의미하는 것이고, 이는 다음과 같은 일이 가능함을 의미하는 것이다.
"A 쓰레드가 만든 인스턴스의 참조 값(사실상 주소 값)만 알면 B 쓰레드도 A 쓰레드가 만든 인스턴스에 접근 가능하다."
그래서 쓰레드 사이에 데이터를 주고받아야 할 때에는(쓰레드 간 통신이 필요할 때에는) 힙 영역을 활용한다. 그럼 간단한 예제를 통해서 둘 이상의 쓰레드가 힙에 할당된 특정 메모리 영역에 함께 접근하는 예를 보이겠다.
class Sum
{
int num;
public Sum() { num = 0; } // 생성자
public void addNum(int n)
{
synchronized(this)
{
num+=n;
}
}
public int getNum() { return num; }
}
class AdderThread extends Thread
{
Sum sumInst;
int start, end;
public AdderThread(Sum sum, int s, int e)
{
sumInst = sum;
start = s;
end = e;
}
public void run()
{
for(int i = start; i <= end; i++)
sumInst.addNum(i);
}
}
class ThreadHeapMultiAccess
{
public static void main(String[] args)
{
/*
아래의 코드는 Sum 인스턴스의 참조 부분을 at1, at2에서 생성하는 쓰레드 인스턴스에 생성자를 통해서 전달한다.
*/
Sum s= new Sum();
AdderThread at1 = new AdderThread(s, 1, 50);
AdderThread at2 = new AdderThread(s, 51, 100);
at1.start();
at2.start();
try {
at1.join();
at2.join();
} catch (Exception e) {
//TODO: handle exception
e.printStackTrace();
}
System.out.println("1~100까지의 합 : "+s.getNum());
}
}
사실 위 예제는 메모리 구성을 모르는 상태에서도 이해할 수 있다. 하지만 이러한 코드 구성이 가능한 이유가 메모리 구성에 있다는 것을 알아 둘 필요는 있다. 만약에 쓰레드 별로, 스택과 마찬가지로 독립된 힙이 할당되었다면, 위 예제의 실행결과는 어떠했겠는가?위 예제에서 보였듯이 31행에서 생성한 인스턴스의 참조 값을 두 쓰레드에 전달하는 것은 가능하다. 단! 각각의 쓰레드가 이 참조 값을 이용해서 인스턴스에 접근할 때에는 문제가 생기게 된다. 쓰레드 자신에게 할당된 힙에서 인스턴스를 찾으려고 할 테니 말이다.
* <<나는 정말 JAVA를 공부한 적이 없어요>>의 674-676p에 나와있는 내용입니다.
'프로그래밍 언어(Programming Language) > Java' 카테고리의 다른 글
Keywords of Java such as private, default, protected and public (0) | 2020.01.21 |
---|---|
[Java] Try catch 그리고 finally!? (0) | 2020.01.18 |
Oracle JDK vs Open JDK (0) | 2020.01.17 |