스터디 팀원과 같이 선착순 예약 관리 기능을 구현하기 위해
얘기하다가 어이없는 부분에서 막혀서 이를 정리해보려고 한다.
내가 뭘 모르고 왜 헷갈려 하는지를 제대로 명확히 알지 못해서 나왔던 의문...
스프링에서 빈은 싱글톤 즉, 하나다.
- 그러면 해당 빈 안에 있는 메서드도 하나만 생성되는거 아냐? (심각했다...) <- 이 부분에서 나는 아주 큰 착각을 했다.
- 스프링은 기본적으로 싱글톤으로 관리하는데 싱글톤이면 하나의 인스턴스만 생성되는데, 여러 요청 스레드가 동시에 해당 싱글톤 객체의 메서드를 호출을 하면 호출자체가 안되는거 아냐?
그렇게 바보 같은 의문이 꼬리에 꼬리를 물었다. (지금이라도 알았다니 다행이다. 부끄럽다...)
동기 비동기 얘기를 하다가 많이 어지러웠나부다...(전혀 다른 부분인데.)
스프링에서 빈(Bean)은 기본적으로 "싱글톤"이다.
스프링 IoC 컨테이너당 정확히 하나의 Bean 인스턴스만 생성하고 관리한다는 의미이다.
해당 싱글톤 인스턴스는 모든 요청에서 공유되고, 애플리케이션 내에서 필요할 때마다 삽입된다.(의존관계가 주입된다.)
더불어 싱글톤 범위는 스레드가 아닌 컨테이너당 단일 인스턴스를 보장하기에 모든 스레드는 하나의 인스턴스를 공유한다.
자바에서 메서드 저장과 실행
메서드의 저장은? 메서드 영역에서! (까먹고 있었다.)
우선, 자바를 기준으로 정리해보면
자바 객체의 메서드(메서드의 컴파일된 코드)는 JVM 메모리의 메서드 영역에 저장된다.
스프링이라고 다를까? 당연히 전혀 아니다...
즉, 스프링 IoC 컨테이너로 생성 및 관리되는 싱글톤 Bean의 메서드도 (이것도 인스턴스의 메서드니까 당연히)
메서드 영역에 한 번 저장된다.
결국 JVM 내의 메서드 영역에 대한 존재를 잊고 있었다.
정리하면 객체의 메서드는 메서드 영역에 저장된다.
메서드 실행은? 스택 영역에서 (이건 알고 있었지.)
자 그러면. 메서드 영역에 저장된 메서드를
각각의 스레드에 의해 호출되면 어떻게 될까?
우선, 각각의 스레드에는 자체 스택이 있다.
스레드가 메서드를 호출하면, 스레드의 자체 스택에 새로운 스택 프레임을 푸시한다.
참고로, 스택 프레임에는 메서드 실행에 필요한 지역 변수 및 기타 정보가 포함되어 있다.
그렇기에 동시에 호출할 수 있다.
각 스레드의 자체 스택을 통해 메서드가 실행되기에
여러 스레드가 동시에 같은 메서드를 호출할 수 있는 것이다.
JVM은 메서드 및 객체에 대한 다중 스레드의 액세스를 관리한다.
JVM 내부 메모리 구조에 대해 블로그에 포스팅까지 했지만,
이에 대해 제대로 알지 못했던 것 같다... (반성 중...)
https://k9want.tistory.com/entry/JVM-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0
JVM 내부 구조
JVM은 자바 바이트코드를 실행시키는 가상 컴퓨터로, 자바의 "한 번 작성하면 어디서나 실행된다"는 특징을 실현합니다. 주요 구성 요소 Class Loader SubSystem 클래스 로더는 자바의 런타임 중에 필요
k9want.tistory.com
이제 그러면 합쳐보자. 싱글톤 + 동시에 메서드 호출 = 동시성 문제 발생 ㅠㅠ
일단! 잘못된 생각부터 정리하자!
단순 스프링 Bean의 싱글톤 특성은 Bean 자체의 인스턴스화(만드는데)에만 집중한다.
즉, 해당 Bean 내 메서드의 스레드 안전(thread safe)이나 동시성 동작에는 아무런 영향을 미치지 않는다.
따라서 두 요청 스레드가 싱글톤 인스턴스의 동일한 메서드를 호출하는 경우 이를 방지하기 위한 조치를 취하지 않는 한 해당 메서드 호출이 동시에 실행될 수 있습니다.
그렇기에 메서드가 힙의 공유 데이터(싱글톤 인스턴스의 인스턴스 변수)를 액세스하거나 수정하는 경우에
동기화가 제대로 되어있지 않으면,여러 스레드에서 수정 메서드를 동시에 실행하게 되고
이는 곧 동시성 문제(일관되지 않은 상태나 race condition)가 필연적으로 발생하게 되는 것이다.
정리
- 스프링 IoC 컨테이너가 생성하고 관리하는 싱글톤 Bean은 기본적으로 단 하나의 인스턴스만 사용되도록 보장하지만,메서드 실행 중 스레드 안전성이나 상호 배제에 대한 어떠한 보장도 제공하지 않는다.
- 각각의 스레드는 자체 스택을 가지고 있어서 메서드 영역에 존재하는 동기화되지 않은 동일한 메서드를 호출하면 각 스레드 안에 존재하는 자체 스택 안에서 실행된다. 따라서 동시에 메서드를 호출할 수 있다.
- 이로 인해 동시성 문제가 생길 수 있고, 적절한 동기화 처리가 필요하다.
잘못된 부분이 있다면 댓글 남겨주세요. 부탁드려요
'메모' 카테고리의 다른 글
[메모] 테스트 코드에서 lombok 사용하려면? (0) | 2024.04.19 |
---|