生成随机数的类Random和ThreadLocalRandom

转载 2018年01月18日 20:22:20

java里有伪随机型和安全型两种随机数生成器,伪随机生成器根据特定公式将seed转换成新的伪随机数据的一部分,安全随机生成器在底层依赖到操作系统提供的随机事件来生成数据。

安全随机生成器

  • 需要生成加密性强的随机数据的时候才用它
  • 生成速度慢
  • 如果需要生成大量的随机数据,可能会产生阻塞需要等待外部中断事件

而伪随机生成器,只依赖于“seed”的初始值,如果给生成算法提供相同的seed,可以得到一样的伪随机序列。一般情况下,由于它是计算密集型的(不依赖于任何IO设备),因此生成速度更快。以下是伪随机生成器的进化史。

java.util.Random
自1.0就已经存在,是一个线程安全类,理论上可以通过它同时在多个线程中获得互不相同的随机数,这样的线程安全是通过AtomicLong实现的。
Random使用AtomicLong CAS(compare and set)操作来更新它的seed,尽管在很多非阻塞式算法中使用了非阻塞式原语,CAS在资源高度竞争时的表现依然糟糕,后面的测试结果中可以看到它的糟糕表现。

java.util.concurrent.ThreadLocalRandom
1.7增加该类,企图将它和Random结合以克服所有的性能问题,该类继承自Random。

ThreadLocalRandom的主要实现细节:

  • 使用一个普通的long而不是使用Random中的AtomicLong作为seed
  • 不能自己创建ThreadLocalRandom实例,因为它的构造函数是私有的,可以使用它的静态工厂ThreadLocalRandom.current()
  • 它是CPU缓存感知式的,使用8个long虚拟域来填充64位L1高速缓存行

测试

下面进行5种测试:

  1. 一个单独的Random被N个线程共享
  2. ThreadLocal<Random>
  3. ThreadLocalRandom
  4. Random[], 其中每个线程N使用一个数组下标为N的Random
  5. Random[], 其中每个线程N使用一个数组下标为N * 2的Random

所有的测试都使用封装在RandomTask类里的方法,每个方案都说明了如何使用随机生成器。

import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;

public class Test_Random {

    private static final long COUNT = 10000000;
    private static final int THREADS = 2;
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println("Shared Random");
        testRandom(THREADS, COUNT);
        /*System.out.println("ThreadLocal<Random>");
        testThreadLocal_Random(THREADS, COUNT);
        System.out.println("ThreadLocalRandom");
        testThreadLocalRandom(THREADS, COUNT);
        System.out.println("Shared Random[] with no padding");
        testRandomArray(THREADS, COUNT, 1);
        System.out.println("Shared Random[] with padding");
        testRandomArray(THREADS, COUNT, 2);*/
    }

    private static class RandomTask implements Runnable {
        private final Random rnd;
        protected final int id;
        private final long cnt;
        private final CountDownLatch latch;

        private RandomTask(Random rnd, int id, long cnt,
                CountDownLatch latch) {
            super();
            this.rnd = rnd;
            this.id = id;
            this.cnt = cnt;
            this.latch = latch;
        }

        protected Random getRandom() {
            return rnd;
        }

        @Override
        public void run() {
            try {
                final Random r = getRandom();
                latch.countDown();
                latch.await();
                final long start = System.currentTimeMillis();
                int sum = 0;
                for (long j = 0; j < cnt; j++) {
                    sum += r.nextInt();
                }
                final long time = System.currentTimeMillis() - start;
                System.out.println("Thread #" + id + " Time = " + time / 1000.0 + " sec, sum = " + sum);
            } catch (InterruptedException e) {}
        }
    }

    private static void testRandom(final int threads, final long cnt) {
        final CountDownLatch latch = new CountDownLatch(threads);
        final Random r = new Random(100);
        for (int i = 0; i < threads; ++i) {
            final Thread thread = new Thread(new RandomTask(r, i, cnt, latch));
            thread.start();
        }
    }

    private static void testRandomArray(final int threads, final long cnt, final int padding) {
        final CountDownLatch latch = new CountDownLatch(threads);
        final Random[] rnd = new Random[threads * padding];
        for (int i = 0; i < threads * padding; ++i) {
            rnd[i] = new Random(100);
        }
        for (int i = 0; i < threads; ++i) {
            final Thread thread = new Thread(new RandomTask(rnd[i * padding], i, cnt, latch));
            thread.start();
        }
    }

    private static void testThreadLocalRandom(final int threads, final long cnt) {
        final CountDownLatch latch = new CountDownLatch(threads);
        for (int i = 0; i < threads; ++i) {
            final Thread thread = new Thread(new RandomTask(null, i, cnt, latch) {
                @Override
                protected Random getRandom() {
                    // TODO Auto-generated method stub
                    return ThreadLocalRandom.current();
                }
            });
            thread.start();
        }
    }

    private static void testThreadLocal_Random(final int threads, final long cnt) {
        final CountDownLatch latch = new CountDownLatch(threads);
        final ThreadLocal<Random> rnd = new ThreadLocal<Random>() {

            @Override
            protected Random initialValue() {
                // TODO Auto-generated method stub
                return new Random(100);
            }

        };
        for (int i = 0; i < threads; ++i) {
            final Thread thread = new Thread(new RandomTask(null, i, cnt, latch) {

                @Override
                protected Random getRandom() {
                    // TODO Auto-generated method stub
                    return rnd.get();
                }

            });
            thread.start();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118

总结:

  • 任何情况下都不要在多个线程间共享一个Random实例,而该把它放入ThreadLocal之中
  • java7在所有情形下都更推荐使用ThreadLocalRandom,它向下兼容已有的代码且运营成本更低

使用ThreadLocalRandom产生并发随机数

Java 7之前我们使用Math.random()产生随机数,使用原子变量来保存当前的种子,这样两个线程同时调用序列时得到的是伪随机数,而不是相同数量的两倍。 ThreadLocalRa...
  • u010739551
  • u010739551
  • 2016年03月23日 09:34
  • 834

java7新特性——使用ThreadLocalRandom产生并发随机数

Java 7之前我们使用Math.random()产生随机数,使用原子变量来保存当前的种子,这样两个线程同时调用序列时得到的是伪随机数,而不是相同数量的两倍。 ThreadLocalRand...
  • kuyuyingzi
  • kuyuyingzi
  • 2014年12月10日 23:33
  • 3286

生成随机数的类Random和ThreadLocalRandom

java里有伪随机型和安全型两种随机数生成器,伪随机生成器根据特定公式将seed转换成新的伪随机数据的一部分,安全随机生成器在底层依赖到操作系统提供的随机事件来生成数据。安全随机生成器 需要生成加密性...
  • u013115610
  • u013115610
  • 2017年06月21日 10:04
  • 1603

[疯狂Java]基础类库:Random(随机数生成)、ThreadLocalRandom(线程安全随机数生成)

1. Random类的使用方法:     1) 和其它语言一样,Java的Random其实也是一个为随机序列生成器,同样需要一个序列种子作为序列的开关,如果种子相同则产生的序列相同;     2) 构...
  • Lirx_Tech
  • Lirx_Tech
  • 2016年05月22日 14:18
  • 2364

使用Random类生成随机数

java中,除了可以通过random 方法来产生随机数之外,还可以通过一个random类来产生随机数。通过实例化一个Random对象来创建一个随机数的生成器。如 Random i=new Random...
  • panes
  • panes
  • 2014年05月26日 14:54
  • 773

Java中生成随机数Random、ThreadLocalRandom、SecureRandom、Math.random()

我们来说说java常见的生成随机数的几种方式:Random,ThreadLocalRandom,SecureRandom;其实产生随机数有很多种方式但我们常见的就这几种,如果需要详细了解这个三个类,可...
  • admin1973
  • admin1973
  • 2017年02月14日 10:39
  • 2217

用Random类产生随机数

源代码: import java.util.Random; import java.util.Arrays; public class RandomTest { public static voi...
  • hcy2319964421
  • hcy2319964421
  • 2016年05月23日 21:17
  • 2082

Java并发编程-34-生成并发随机数-ThreadLocalRandom

一、ThreadLocalRandom 这是一个线程本地变量,每个生成随机数的线程都有一个不同的生成器,但是都在同一个类中被管理。 current()方法:这是一个静态方法,返回与当前线程...
  • u012730315
  • u012730315
  • 2015年06月21日 11:19
  • 1751

Random ThreadLocalRandom 产生随机数

使用方法。都是new出来的,实例方法。需要对象来调用方法 对象.nextXxx();可以产生各种基本类型。...
  • hlowuqqvwy
  • hlowuqqvwy
  • 2017年09月20日 19:42
  • 58

使用/dev/random生成随机数

很多库例程产生的“随机”数是准备用于仿真、游戏等等;它们在被用于密钥生成一类的安全函数时是不够随机的。其问题在于这些库例程使用的算法的未来值可以被攻击者轻易地推导出来(虽然看起来它们可能是随机的)。对...
  • lme525
  • lme525
  • 2014年07月10日 16:13
  • 827
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:生成随机数的类Random和ThreadLocalRandom
举报原因:
原因补充:

(最多只允许输入30个字)