[Spring] 엔티티 필드의 동시성 문제

bagt13·2022년 11월 21일
0

Project

목록 보기
16/17
post-thumbnail

문제의 객체 필드

@Entity
@Getter
@NoArgsConstructor(access = PROTECTED)
@AllArgsConstructor
public class Member extends Auditable {

    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long id;

    private Long totalRecord;

member 엔티티의 필드로 회원의 누적 운동 시간(totalRecord)를 가지고 있었고, totalRecord에 대한 요청이 왔을 때 Dto에서도 totalRecord 필드를 바로 반환하고 있었다.


📘 싱글톤 패턴 (Singleton Pattern)

Spring Bean은 싱글톤(Singleton)으로 관리되고, 이는 애플리케이션에 딱 하나만 존재한다는 뜻이다.


❗️ 동시성 이슈

주의해야 할 점은 객체 필드가 상태값을 가지도록, 즉 stateful하도록 설계해서는 안된다.

여기서 발생한 문제 또한 statful한 인스턴스의 필드를 여러 thread에서 동시에 접근하면서 발생한 문제이다.


thread는 stack 영역을 제외한 프로세스의 메모리 영역을 공유하기 때문이다.

따라서 각 thread는 별도의 stack 영역을 갖고 있기 때문에, 지역 변수 또는 매개변수에 대해서는 동시성 문제가 발생하지 않는다.


반면, 인스턴스의 필드는 프로세스의 Data 영역에 저장되기 때문에 동시성 이슈로부터 자유롭지 못한 것이다.

  • 동시성 문제는 인스턴스의 필드 또는 static 필드에 접근할 때 발생한다.

  • 이러한 동시성 문제는 지역 변수에서는 발생하지 않는다.

    • 자바에서 지역 변수(stack)는 thread마다 각각 다른 메모리 영역을 할당하기 때문이다. 또한, 읽기에 대해서는 동시성 문제가 발생하지 않으며, 다른 곳에서 값을 변경할 때 문제가 생긴다.
  • 특히 스프링 빈 처럼 Singleton 객체의 필드를 변경하며 사용할 때 이러한 동시성 문제를 조심해야 한다.

여기서도 인스턴스 필드에 여러 thread에서 동시에 접근하게 될 경우 문제가 발생할 수 있다.



✅ 해결 방법

  1. ThreadLocal 사용
  2. Synchronized 사용
  3. 인스턴스의 필드가 상태값을 갖지 않도록 설계 수정

현재 나의 상황에서는 3번 방법이 그리 큰 작업이 아니었기 때문에 이 방법으로 금방 해결할 수 있었다. 하지만 만일 비즈니스상 상태값을 가져야만 하는 상황이거나, 수정하는 데에 큰 비용이 드는 상황이라면 다른 방법이 필요하다.

그래서 호기심에 여러가지 방법을 찾아보았다.


📙 ThreadLocal 사용

ThreadLocal이란?

특정 thread만 접근할 수 있는 특별한 저장소로써, 해당 thread를 식별하여 thread마다 저장소를 구분할 수 있다.

❗️ ThreadLocal 사용 시 주의점

잘못 사용하면 메모리 누수를 일으켜 큰 장애를 야기할 수 있다

예를 들어, thread를 사용 후 threadpool에 반환할 때 remove()를 통해 반드시 초기화해야 한다. 톰캣의 경우 threadpool에 반환 후, 다른 곳에서 재사용되기 때문에, 이전의 데이터를 초기화해주지 않으면 다른 곳에서 그대로 사용하게 된다.


📙 Synchronized 사용

Java의 Synchronized 를 사용하여 동기화를 수행할 수 있다.

Synchronized 사용 시 고려해야 할 점

  • Synchronized는 하나의 프로세스 안에서만 보장되기 때문에 서버가 여러개일 경우 효과를 볼 수 없으며, 추가적인 작업이 필요하다.

  • 성능 저하가 발생할 수 있기 때문에, 꼭 필요한 경우에 잘 사용해야 한다.

profile
주니어 백엔드 개발자입니다😄

0개의 댓글