자바에서 제공하는 랜덤 클래스에는 Random과 ThreadLocalRandom이 있다.
두 클래스의 작동 방식과 어떤 차이점이 있는지 비교해 보겠다.
Random
난수 생성 방법
Random 내부의 코드를 살펴보면 seed를 어떻게 생성하는지 살펴볼 수 있다.
public Random() {
this(seedUniquifier() ^ System.nanoTime());
}
Random은 사용자가 시드값을 인자로 넘기지 않는다면 nanotime, 즉 jvm의 고해상도 시간 측정 장치의 값을 기준으로 난수를 생성한다.
seed 값을 변하지 않는 고정값으로 정한다면 동일한 패턴의 난수를 생성하기 때문에 계속 변하는 값인 시간을 seed로 사용한다.
멀티 스레드 환경에서의 문제점
Random은 하나의 인스턴스를 공유하여 사용한다.
따라서 만약 멀티 스레드 환경에서 동일한 nanotime에 난수 요청이 들어온다면 어떻게 될까?
만약 같은 seed값을 사용한다면 여러 요청에 같은 난수를 반환하게 될 것이다.
Random은 내부적으로 동일한 nanotime에 여러 요청이 오는 경우 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));
}
Random의 모든 nextXXX 메서드는 next메서드를 호출하여 랜덤 값을 반환한다.
여러 스레드가 이 객체를 동시에 접근해도 안전하게 수행할 수 있도록 AtomicLong, compareAndSet을 사용하는 것을 볼 수 있다.
만약 멀티 스레드 환경에서 동시에 난수 요청이 들어온다면 스레드 간에 경합을 통해 난수를 생성하기 때문에 성능 저하가 발생할 수 있다.
ThreadLocalRandom
ThreadLocalRandom은 Random의 문제점을 해결하기 위해 JAVA7에 추가된 Random을 상속받는 구현체이다.
따라서 사용방법은 Random과 거의 동일하며 어떤 것이 달라졌는지 알아보자.
작동 방식
ThreadLocalRandom은 멀티 스레드 환경에서 각각의 스레드마다 서로 다른 인스턴스를 할당한다.
따라서 Random에서 문제였던 스레드 간의 경합으로 인한 성능 저하가 발생하지 않아 빠른 시간에 랜덤 값을 반환할 수 있다.
private static final ThreadLocalRandom instance = new ThreadLocalRandom();
public static ThreadLocalRandom current() {
if (U.getInt(Thread.currentThread(), PROBE) == 0)
localInit();
return instance;
}
멀티 스레드 환경에서는 Random 대신 ThreadLocalRandom을 사용하는 것이 효율적으로 난수를 관리할 수 있기에 사용하는 것을 추천한다.
'Java' 카테고리의 다른 글
[Java] List 복사 방법과 불변 리스트, unmodifiableList와 copyOf (1) | 2024.03.04 |
---|