ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 🆚 Random 함수 비교
    ☕️ java 2023. 10. 23. 12:36

     

    숫자야구 프로그램을 만들면서 Java에서 제공하는 Random 함수들이 어떤 것들이 있는지 궁금하고 어떤 것을 사용해야할지 궁금했다.

     

    1️⃣ java.util.Random


    💡 java.util 패키지에 있는 Random 클래스를 사용하는 방법
     - 인스턴스를 생성해서 사용 

     

    🖥️ 사용

    • 2가지 객체 생성 방법
      • 기본 생성자를 사용하는 방법
      • long 타입의 시드를 인자로 받는 생성자를 사용하는 방법
    // 기본 생성자
    Random random = new Random();
    
    // long 타입의 시드를 인자로 받는 생성자
    Random random = new Random(10);
    
    // 난수 범위 지정
    int randomNumber = random.nextInt(max - min) + min;

     

    ⚠️ 주의할 점

    • long 타입의 seed 값을 설정해 Random 클래스를 사용할 때 주의할 점이 존재
    for (int i = 0; i < 5; i++) {
        Random random = new Random(10);
        for (int j = 0; j < 5; j++) {
            System.out.print(random.nextInt() + " ");
        }
        System.out.println();
    }

     

    결과

    -1157793070 1913984760 1107254586 1773446580 254270492 
    -1157793070 1913984760 1107254586 1773446580 254270492 
    -1157793070 1913984760 1107254586 1773446580 254270492 
    -1157793070 1913984760 1107254586 1773446580 254270492 
    -1157793070 1913984760 1107254586 1773446580 254270492
    

    동일한 seed 값을 갖는 인스턴스가 생성한 난수는 일정 패턴을 가지게 된다…!

     

     

    🤔 why?

    유사 난수 생성기(pseudo-random-generator)를 통해 실제로 무작위가 아닌 일련의 숫자를 생성하게 된다.

    public Random(long seed) {
        if (getClass() == Random.class)
            this.seed = new AtomicLong(initialScramble(seed));
        else {
            // subclass might have overridden setSeed
            this.seed = new AtomicLong();
            setSeed(seed);
        }
    }
    
    private static long initialScramble(long seed) {
        return (seed ^ multiplier) & mask;
    }
    • 특정 알고리즘으로 생성된 값을 반환하게 되는 것이다.

     

     

    ❗ seed 값을 설정하지 않은 경우는 현재 시간을 활용한다.

    public Random() {
        this(seedUniquifier() ^ System.nanoTime());
    }

     

    ⚠️ 멀티 쓰레드에서 위험

    java.util.Random 은 멀티 쓰레드에서 하나의 Random 인스턴스를 공유하여 전역으로 사용된다.

     

    이때, 같은 시간에 멀티 쓰레드에서 요청이 들어온다면❓

    • 같은 난수를 반환하지는 않는다.
    • 하지만, 여러 쓰레드가 경합을 하는 과정을 반복하게 되어 성능상의 문제가 발생할 수 있다.

     

    2️⃣ Math.random 메서드


    💡 Math.random 메서드는 객체 생성 없이 바로 사용할 수 있는 정적 메서드

     

    📍 반환 값

    • 0.0 ≤ .. ≤ 1.0 보다 작은 double 형 값
    • 현재 시간을 seed 값 으로 사용해 매 실행마다 다른 난수가 반환된다.

     

    🖥️ 사용

    // 0.0보다 크거나 같고 1.0보다 작은 값
    double randomNumber = Math.random(); 
    
    // min값보다 크거나 같고 max값보다 작은 난수
    int randomNumber = (int) (Math.random() * (max - min)) + min);
    

     

    📖 정적 메서드

    • 인스턴스 생성 없이 호출 가능

     

    3️⃣ ThreadLocalRandom

    💡 ThreadLocalRandom
    - 현재 스레드에 격리된 난수 생성기
    - ThreadLocal 클래스와 Random 클래스의 조합
    - java.util.concurrent.ThreadLocalRandom

     

    public class ThreadLocalRandom extends Random {
    	  ...
        public void setSeed(long seed) {
            // only allow call from super() constructor
            if (initialized)
                throw new UnsupportedOperationException();
        }
    		...
    }
    

     

    💡 특징

    • Math.random 메서드와 마찬가지로 수정할 수 없는 내부 생성 seed 로 초기화
      • setSeed 메서드를 재정의하여 호출된 경우 UnsupportedOperationException 예외 호출
    • 동시 프로그램에서 Random 클래스를 사용한 것보다 일반적으로 오버헤드와 경합이 적다.
      • 스레드 풀에서 난수를 병렬로 사용시 적절
    • 암호적으로 안전하지 않다. 보안에 민감한 응용프로그램에서는 SecureRandom을 사용하는 것을 고려

     

    🆚 Random 클래스

    다중 스레드 환경에서 두 클래스(Random, ThreadLocalRandom)를 사용해 임의의 값을 생성하고 JMH를 사용해 성능을 비교해보기.

    @State(Scope.Benchmark)
    public class Sample {
    
        private static final int N = 1000;
    
        @Benchmark
        public void testRandom(Blackhole blackhole) {
            Random random = new Random();
            for (int i = 0; i < N; i++) {
                int result = random.nextInt();
                blackhole.consume(result);
            }
        }
    
        @Benchmark
        public void testThreadLocalRandom(Blackhole blackhole) {
            for (int i = 0; i < N; i++) {
                int result = ThreadLocalRandom.current().nextInt();
                blackhole.consume(result);
            }
        }
    
        public static void main(String[] args) throws Exception {
            org.openjdk.jmh.Main.main(args);
        }
    }
    

     

    결과(results.txt)

    Benchmark                      Mode  Cnt       Score   Error  Units
    Sample.testRandom             thrpt       105661.179          ops/s
    Sample.testThreadLocalRandom  thrpt       294349.298          ops/s
    
    • ThreadLocalRandom을 사용했을 경우 더 효율적으로 동작!!

     

    📖 JMH

    • JVM 위에서 동작하는 코드의 성능을 측정해주는 라이브러리
    • 간단한 코드나 여러 코드의 상대적 성능을 측정할 때 간단히 사용

     

    🖥️ 사용

    int randomNumber = ThreadLocalRandom.current().nextInt());
    
    // 1과 10 사이의 난수
    int boundedRandomValue = ThreadLocalRandom.current().nextInt(1, 10);

     

    4️⃣ SecureRandom


    • java.security.SecureRandom
    • 더 강력한 의사난수 생성기를 이용해 난수를 생성

     

    🖥️ 사용

    SecureRandom secureRandom = new SecureRandom();
    
    int randomInt = secureRandom.nextInt();
    
    // 1과 10 사이의 난수
    randomInt = secureRandom.nextInt(10 - 1) + 1

     

    🆚 Random

    1. Random 클래스는 시스템 시간을 시드로 사용하거나 시드를 생성
      • SecureRandom은 OS의 무작위 데이터를 가져와 시드로 사용한다.
    2. 48비트를 갖는 Random과 다르게 SecureRandom은 최대 128비트를 포함한다.
      • 반복될 확률이 적다.
    3. 내부에서 다양한 난수 생성과정을 거쳐 Random 클래스보다 느리다

     

    📕 참고자료

    ThreadLocalRandom (Java Platform SE 8 )

    Guide to ThreadLocalRandom in Java | Baeldung

    JMH: Java Benchmark Tool

    GitHub - openjdk/jmh: https://openjdk.org/projects/code-tools/jmh

    [Java] JMH(Java Microbenchmark Harness) 로 성능 벤치마킹

    Random 대신 ThreadLocalRandom을 써야 하는 이유

    '☕️ java' 카테고리의 다른 글

    일급 컬렉션  (1) 2023.10.24
    🧪 JMH (Java Microbenchmark Harness)  (1) 2023.10.23
    DAO DTO / VO  (0) 2023.03.10
    [Java] 실수 표현 - 고정소수점, 부동소수점  (0) 2023.03.07
    BigDecimal ❓  (0) 2023.03.07

    댓글

Designed by Tistory.