this 키워드와 자바에서 스레드의 동작 방식, 스레드가 인스턴스 메서드를 호출할 때 this를 통해 해당 인스턴스를 어떻게 기억하고 사용하는지를 설명해보겠습니다.
자바에서 스레드가 메서드를 호출하면 스택 프레임(Stack Frame)이라는 것이 생성됩니다. 스택 프레임은 각 메서드 호출 시 메서드 실행에 필요한 매개변수, 지역 변수, 리턴 주소 등의 정보가 저장되는 메모리 영역입니다.
스택 프레임 구조:
따라서, 각 스레드가 메서드를 호출하면 JVM은 해당 메서드에 대한 스택 프레임을 만들고, 이때 인스턴스 메서드의 경우 해당 인스턴스의 참조값(this)도 스택 프레임에 저장합니다. 이를 통해 스레드는 자신이 어떤 인스턴스에서 메서드를 호출했는지 기억하고, 인스턴스 필드나 메서드에 (힙에 있는) 접근할 수 있게 됩니다.
this는 현재 메서드가 소속된 객체의 참조값입니다. 예를 들어, 인스턴스 메서드가 호출될 때, 어떤 객체의 메서드가 호출되었는지를 나타냅니다. 인스턴스 메서드 내에서 this를 사용하면, 그 메서드를 호출한 객체(인스턴스)를 가리키며, 이를 통해 해당 객체의 필드나 다른 메서드에 접근할 수 있습니다.
예시:
class MyClass {
private String name;
public MyClass(String name) {
this.name = name; // this는 현재 객체를 가리킴
}
public void printName() {
System.out.println(this.name); // this를 통해 인스턴스 필드에 접근
}
}
여기서, this는 현재 객체를 가리키는 참조값으로, printName() 메서드가 실행될 때 해당 객체의 name 필드를 참조하게 됩니다.
스레드가 인스턴스 메서드를 호출할 때, 스택 프레임에 저장된 this를 사용해 해당 인스턴스를 식별하고 그 객체의 필드와 메서드에 접근합니다.
스레드 예시:
class MyRunnable implements Runnable {
private int counter;
@Override
public void run() {
for (int i = 0; i < 5; i++) {
counter++;
System.out.println(Thread.currentThread().getName() + " - Counter: " + this.counter);
}
}
}
public class Main {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread1 = new Thread(runnable, "Thread 1");
Thread thread2 = new Thread(runnable, "Thread 2");
thread1.start();
thread2.start();
}
}
위 코드에서 두 개의 스레드(thread1과 thread2)가 같은 MyRunnable 인스턴스를 공유하고 있습니다. 각 스레드는 독립적인 실행 흐름을 가지고 있지만, 공유된 Runnable 객체의 메서드를 실행합니다.
중요한 포인트:
이때 this는 각 스레드가 메서드를 호출한 객체를 정확히 기억하고 있으므로, 같은 객체에 접근하고 수정하게 됩니다.
이 상황에서는 스레드 간 경쟁 조건(Race Condition)이 발생할 수 있으며, 이를 방지하기 위해서는 동기화(synchronization)가 필요합니다.
"스레드 1과 스레드 2가 자신의 인스턴스를 구분할 수 있다"는 표현은, 각 스레드가 this를 사용하여 자신이 현재 어떤 인스턴스의 메서드를 호출하고 있는지 구분할 수 있다는 의미입니다. 스레드가 같은 Runnable 객체를 공유하더라도, 스택 프레임에 저장된 this 값을 통해 인스턴스를 정확하게 참조할 수 있습니다.
예를 들어, 스레드가 run() 메서드를 실행할 때 스택 프레임에 저장된 this 값을 참조하여, 어느 객체에서 메서드가 실행되었는지를 기억하고, 그 객체의 필드나 메서드를 사용하게 됩니다.
this는 필드에 접근할 때 자동으로 사용됩니다. 즉, 인스턴스 메서드에서 필드에 접근하는 모든 경우에 사실상 this가 암시적으로 사용되고 있는 것입니다. 아래 예시를 통해 이를 설명할 수 있습니다:
예시:
class MyClass {
private int count;
public void increment() {
count++; // 실제로는 this.count++ 이라고 동작
}
}
위 코드에서 count++는 사실 this.count++와 동일한 동작을 합니다. 자바에서는 인스턴스 필드에 접근할 때 자동으로 this를 사용하여 현재 객체의 필드에 접근하게 됩니다. 따라서 스레드가 메서드를 실행할 때 스택 프레임에 저장된 this를 사용하여 필드를 접근하고 수정하게 됩니다.
정리하면: