ThreadLocalRandom 클래스의 설계 의도는 무엇일까?
Random 클래스는 멀티 스레드에서 하나의 Random 인스턴스를 공유하며 전역적을 동작합니다.
seed를 통해서 난수를 반환하기 때문에 싱글톤으로 설계하는 게 이점이라 생각해 이렇게 구현되었을 것이라 추측합니다.(개인적 의견)
그런데 이는 문제가 하나 있습니다. 바로, seed가 같으면 같을 경우 같은 난수가 반환될 수도 있다는 것입니다.
Random 클래스에서의 seed는 따로 지정되지 않을 시에 컴퓨터의 현재 시간으로 결정됩니다.
그렇다면, 여러 스레드가 동시에 Random 클래스를 사용할 경우도 분명 있을 것입니다. 이 경우에 같은 난수를 반환할까요?
다행히 Random 클래스에서는 선형 합동 생성기 알고리즘을 사용해서 같은 난수를 반환하지 않고 있습니다.
다만, "여러 스레드가 동시에 Random 클래스를 사용할 때" 같은 난수를 반환하지 않기 위해 스레드간 경합이 발생하는데 이것이 성능에 문제를 일으킬 수 있습니다.
Random 클래스에서 경험이 발생할 수 있는 이유
다음은 Random 클래스의 핵심 메서드인 next() 메서드입니다.
private final AtomicLong seed;
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
위 메서드 seed.compareAndSet(oldseed, nextseed)는 seed의 현재 값이 oldseed와 동일한 경우에만 nextseed로 업데이트하고 true를 반환합니다.
만약 seed의 값이 oldseed와 다르다면 (즉, 다른 스레드에 의해 변경되었다면) false를 반환하고 값을 업데이트하지 않습니다.
(seed의 현재 값이 oldseed와 동일하다는 의미는 다른 스레드가 seed를 변경하지 않았다는 것을 의미합니다.
이렇게 하는 이유는 Random 클래스는 자신의 seed를 기준으로 난수를 생성하는 데, 기존 스레드가 아닌 다른 스레드가 seed를 변경한다면, seed가 변경되었기 때문에 기존에 나왔던 수가 나올 수 있습니다.
이는 난수가 나올 수 있게 하였지만, 멀티 스레드 환경에서 동시에 요청하는 스레드가 많을 경우 스레드간의 경합이 발생되어, 성능저하가 발생할 수 있다는 단점이 있습니다.
따라서, 멀티 스레드 환경에서는 ThreadLocalRandom 클래스를 사용해야합니다.
ThreadLocalRandom은 뭐가 다른데?
ThreadLocalRandom은 Random에서 발생하는 동시성 문제를 해결하기 위해 각 스레드마다 생성된 인스턴스에 각각 난수를 반홥니다. 이렇게 하면 멀티 스레드 환경에서 동시에 난수를 발행한다고 해도 각 스레드마다 다른 스레드를 사용하기 때문에 경합 문제가 발생하지 않게 됩니다.
그렇다면, 아래의 코드는 옳은 코드일까?
아래 코드는 제가 실제로 작성했었던 코드입니다.
public class PostRandomFetcher {
private static final ThreadLocalRandom random = ThreadLocalRandom.current();
...
}
결론부터 말하자면, 아래 코드는 옳지 않은 코드입니다.. 🥲
먼저, 정적 필드의 특징이 무엇인가??
정적 필드의 특징 중 하나는 모든 인스턴스는 동일한 정적 필드를 공유한다는 것이다.
이말은 정적 필드에 해당 인스턴스를 저장하고 사용하는 것의 의미는 PostRandomFetcher를 사용하는 모든 스레드는 동일한 random 변수를 사용한다는 것이다.
여기서 잠깐!!
ThreadLocalRandomFetcher Class를 사용한 이유가 무엇이었는가??
멀티 스레드 환경에서 동시 요청시 경합 문제를 발생하지 않게 하기 위해서이다.
위와 같이 코드를 작성하게 되면, 정적 필드에 해당 인스턴스를 저장해버리면 모든 스레드가 ThreadLocalRandom 인스턴스를 공유하게 되어 ThreadLocalRandom을 의도대로 사용하지 못 하는 꼴이 되버립니다.
따라서, ThreadLocalRandom 클래스는 아래와 같이 사용해야합니다.
public class PostRandomFetcher {
...
public void example(int param){
int random = ThreadLocalRandom.current.nextInt(param);
...
}
}
전남대_14조_축팅_7주차 by GoBeromsu · Pull Request #139 · Step3-kakao-tech-campus/Team14_BE
글 내용 관련하여 피드백해주신다면 너무 환영입니다!!
'Project Trouble Shooting > [축팅] 축제 소개팅 어플리케이션 - 카카오 테크 캠퍼스 1기' 카테고리의 다른 글
카카오 테크 캠퍼스 수료 및 대상 🏆 (0) | 2023.11.19 |
---|---|
Transaction을 고려한 CheckedException 예외 처리 (3) | 2023.11.12 |
"Run all Tests"로 모든 단위테스트를 한번에 돌릴 때 실패하는 이슈 (0) | 2023.10.14 |
ComposeMethod을 적용해 리팩터링 해보자 (1) | 2023.10.13 |
Instant 클래스 도입에 관한 고찰 (0) | 2023.10.02 |