[ODOP:12]JSR-133(Java Memory Model) 에서 해결하려는 문제

JSR-133 에서 해결하려는 문제를 다룹니다

[!NOTE]
해당 글에서는 JSR 본문의 전체 내용을 포함하지는 않는다
필요하다면 JSR-133번역본 을 참고하여 직접 확인 바란다

(추가로, JSR-133 도입 검토 최종 투표 결과가 있는데 심심하면 봐보는걸 추천. 이만큼 많은 회사에서 투표를 하는지 몰랐다...)

우리가 작성하는 코드는 실제 컴퓨터 에게는 비효율적인 코드가 될 수 있다
이를테면 메모리 캐시와 같은 부분이 그러한데, 아래의 코드로 이야기를 해보자

class Reordering {
    int x = 0, y = 0;
    public void writer() {
        x = 1;
        y = 2;
    }

    public void reader() {
        int r1 = y;
        int r2 = x;
    }
}

위 코드에서 두개의 쓰레드로 writer(), reader() 를 동시에 실행 시킨다고 가정 해 보자

먼저 변수 y2 를 대입하는 연산은 x1 이 대입 된 후 동작 할 것이라고 예측 할 수 있다

하지만 자바의 오래된 reordering 기술의 적용으로 인해 순서가 바뀌어 개발자가 이해하고 있는 코드의 실행 흐름을 따라가지 않을 수 있다

이러한 결함은 Java17 에서 여러가지가 발견 되었는데 그 중 하나의 흐름은 아래와 같다

  1. 위의 코드들이 컴파일러에 의해 Reordering 된다
  2. Thread:A에서 writer 가 실행 되며 y = 2 가 실행 된다
  3. Thread:B에서 reader 가 실행 되며 r1, r2 의 값을 초기화 한다
    • 이 때 r1y 의 초기화가 이루어지고 난 후 대입 되므로 r1 == 2 가 된다
    • 반면에 r2x 의 변경이 이루어지지 않은 후 대입 되므로 r2 == 0 이 된다
  4. 결과적으로 r1 == 2 , r2 == 0 으로 프로그램이 종료 된다

또한 final 키워드가 적용 된 변수의 값이 변경되는 것도 발견이 되었다

[!Final 키워드 문제?]
final 필드가 사용 되었을 때 스레드간 동기화가 필요 없다는 것은 널리 알려진 사실 이다.
이것은 합리적인 가정이며 우리가 당연하게 생각하는 것 이지만 오래된 메모리 모델에서는 그렇지 않다
오래된 메모리 모델은 final 필드를 다른 필드와 다르게 다루지 않는다
즉 개발자가 초기화 하기 이전의 필드를 다른 스레드에서 관찰 할 수 있었으며 final 의 의도와 다르게 값이 변경 되었다
즉, String 과 같은 불변객체의 값이 변화하는 것을 관측 할 수 있었다는 것이다.

JSR-133 > ### What was wrong with the old memory model?

JSR-133 에서는 아래의 목표를 통해 Java 를 개선한다

  • 기존의 타입 안정성 강화
    • 이유 없이 변수의 값들이 생성되는 문제 해결
    • 스레드 합리적인 값 배치
  • 동기화 프로그래밍을 직관적이고 간단한 의미를 가지도록 개선
  • 불완전한 동기화 프로그래밍이 갖는 잠재적 보안 위험을 최소화 하도록 개선
  • 개발자들이 멀티 스레드 프로그램의 메모리 상호작용을 추론 가능하게 개선
  • 다양한 아키텍처에서 고성능 JVM 구현이 가능하도록 개선
  • 초기화 안정성 개선
    • 객체가 생성 되었다면 그 참조값을 가지고 있는 모든 스레드들이 동기화 없이 해당 객체에 접근 가능하도록 개선
  • 기존 코드에는 최소한의 영향만 끼치도록 설계 및 구현