프로그래밍Java 개발자

Java 메모리 모델(JMM)이란 무엇이며 멀티스레드 프로그래밍에 어떤 영향을 미칩니까?

Hintsage AI 어시스턴트로 면접 통과

답변.

문제의 역사:

Java가 처음 등장했을 때 메모리와 스레드 간의 상호작용을 공식적으로 설명하는 내용이 없었습니다. 그 결과 동일한 프로그램이 다른 JVM에서 다르게 동작할 수 있어 찾기 힘든 버그로 이어졌습니다. 2004년 Java 사양에 Java 메모리 모델(JMM)이 추가되어 스레드가 변수 업데이트를 어떻게 인식하는지를 엄격히 정의하기 위해 만들어졌습니다.

문제:

명확한 메모리 모델이 없으면 쓰기 및 읽기 작업이 컴파일러나 프로세서에 의해 섞일 수 있으며, 이로 인해 서로 다른 스레드에서 공유 변수를 접근할 때 예기치 않은 동작이 발생할 수 있습니다. 이는 레이스 조건, 가시성 문제 및 디버깅하기 어려운 동기화 오류를 유발할 수 있습니다.

해결책:

JMM은 한 스레드에서 발생한 변경 사항이 다른 스레드에서 언제 보이는지를 정의하는 규칙을 설정합니다. happens-before 개념, synchronized, volatile, final과 같은 동기화 도구, 내부 잠금 등을 정립하고 멀티스레드 환경에서 명령어의 재구성을 제한합니다.

코드 예시:

class Counter { private int count = 0; public synchronized void increment() { count++; } public synchronized int get() { return count; } }

주요 특징:

  • JMM은 한 변수의 변경 사항이 다른 스레드에 언제 보이는지를 정의합니다.
  • synchronized 또는 volatile 사용은 변경 사항의 올바른 게시를 보장합니다.
  • 잘못된 사용은 명백한 오류가 없어도 잘못된 동작을 초래할 수 있습니다.

의도적인 질문들.

왜 volatile은 작업을 원자적으로 만들지 않고 가시성만 보장합니까?

Volatile은 스레드간 변경 사항의 가시성만 보장하지만 변경 사항의 원자성을 보장하지 않습니다. 예를 들어, volatile 변수를 증가시키는 것은 읽기, 변경 및 쓰기 동작으로 구성되어 있어 원자적이지 않습니다.

코드 예시:

volatile int count = 0; count++; // 원자적이지 않음!

synchronized 블록이 범위를 벗어난 변경 사항의 가시성을 보장할 수 있습니까?

네. synchronized 블록을 벗어난 후 블록 안에서 이루어진 모든 변경 사항은 해당 객체 모니터에 들어가는 다른 스레드에서 볼 수 있습니다.

final 필드의 변경 사항은 언제 다른 스레드에 보이게 됩니까?

final 필드는 객체가 다른 스레드에 연결될 때 완전히 초기화되어 있어야 올바른 게시를 보장합니다. 그렇지 않으면 초기화되지 않은 상태의 가시성이 발생할 수 있습니다.

일반적인 실수 및 안티 패턴

  • 스레드 간 공유되는 변수에 대한 동기화 없이 멀티스레딩 사용
  • 복잡한 작업을 위해 volatile에만 의존
  • 완전하지 않은 객체를 스레드 간에 게시

실생활 사례

부정적인 사례

개발자는 여러 스레드에서 simple volatile 증가를 통해 웹사이트 접근 카운터를 증가시키기로 결정했습니다.

장점:

  • 간단하며 synchronized를 고민할 필요 없음

단점:

  • 원자성이 없어서 높은 부하에서 증가 손실 발생
  • 레이스 조건 및 잘못된 통계

긍정적인 사례

AtomicInteger 또는 synchronized 메서드를 사용하였습니다.

장점:

  • 어떤 부하에서도 정확성을 보장
  • 업데이트 손실 없음

단점:

  • 동기화로 인한 성능 저하가 약간 발생함