Fork/Join模式(JSR166y)手记之ThreadLocalRandom

无人久伴 提交于 2019-12-01 23:43:32
ThreadLocalRandom是一个可以独立使用的、用于生成随机数的类。继承自Random,但性能超过Random,所谓“青出于蓝而胜于蓝”。其API所提供方法,不多,父类Random具有的,它也一样具有。从表明看,是一个单例模式,其实不然:
private static final ThreadLocallocalRandom =
        new ThreadLocal() {
            protected ThreadLocalRandom initialValue() {
                return new ThreadLocalRandom();
            }
    };

    ThreadLocalRandom() {
        super();
        initialized = true;
    }


    public static ThreadLocalRandom current() {
        return localRandom.get();
    }
采用ThreadLocal进行包装的Random子类,每线程对应一个ThreadLocalRandom实例。测试代码:
@Test
 public void testInstance() {
  final ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
  final ListrandomList = new ArrayList();
  final Phaser barrier = new Phaser(1);
  
  new Thread() {
   @Override
   public void run() {
    randomList.add(ThreadLocalRandom.current());
    barrier.arrive();
   }
  }.start();

  barrier.awaitAdvance(barrier.getPhase());
  if (randomList.isEmpty()) {
   throw new NullPointerException();
  }

  Assert.assertTrue(threadLocalRandom != randomList.get(0));
 }
这么一包装,在性能上可以赶超Math.random(),不错。
@Test
 public void testSpeed() {
  final int MAX = 100000;
  ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();

  long start = System.nanoTime();
  for (int i = 0; i < MAX; i++) {
   threadLocalRandom.nextDouble();
  }
  long end = System.nanoTime() - start;
  System.out.println("use time1 : " + end);

  long start2 = System.nanoTime();
  for (int i = 0; i < MAX; i++) {
   Math.random();
  }
  long end2 = System.nanoTime() - start2;
  System.out.println("use time2 : " + end2);

  Assert.assertTrue(end2 > end);
 }
非规范的性能测试,某次输出结果:
use time1 : 3878481
use time2 : 8633080
性能差别不止两倍啊,哈哈。
再看Math.random(),其生成也是依赖于Random类:
private static Random randomNumberGenerator;

    private static synchronized void initRNG() {
        if (randomNumberGenerator == null) 
            randomNumberGenerator = new Random();
    }

    public static double random() {
        if (randomNumberGenerator == null) initRNG();
        return randomNumberGenerator.nextDouble();
    }
很奇怪,性能为什么差那么远呢?可能个各自的next函数不同造成。看一下Random中的next(int bits)方法实现:
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));
    }
而ThreadLocalRandom的重写版本为:
protected int next(int bits) {  
        rnd = (rnd * multiplier + addend) & mask;  
        return (int) (rnd >>> (48-bits));  
    }
相比ThreadLocalRandom的next(int bits)函数实现上更为简练,不存在seed的CAS操作,并且少了很多的运算量。
更为详细的机制研读,请阅读参考资料中链接。
另外,ThreadLocalRandom 也提供了易用的,两个数字之间的随机数生成方式。类似于:
nextDouble(double least, double bound) 
nextInt(int least, int bound) 
nextLong(long least, long bound)
随机数的生成范围为 最小值 <= 随机数 < 最大值。可以包含最小值,但不包含最大值。 
@Test
public void testHowtoUse(){
final ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
final int MAX = 100;
int result = threadLocalRandom.nextInt(0, 100);
Assert.assertTrue(MAX > result);
}
嗯,还有,不支持setSeed方法。
曾经JDK 7中,ThreadLocalRandom 存在随机多个线程随机数生成相同的bug,但最新版本中,已不存在,被修复了,可以放心使用。从现在开始,完全可以使用ThreadLocalRandom替代Random,尤其是在并发、并行、多任务等环境下,会比在多线程环境下使用公共共享的Random对象实例更为有效。

代码清单:

package com.learn.jsry166y.demo.random;

import java.util.ArrayList;
import java.util.List;

import jsr166y.Phaser;
import jsr166y.ThreadLocalRandom;
import junit.framework.Assert;

import org.junit.Test;

/**
 * ThreadLocalRandom简单测试
 * @author yongboy
 * @time 2012-2-2
 * @version 1.0
 */
public class RandomTest {

	@Test
	public void testSpeed() {
		final int MAX = 100000;
		ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();

		long start = System.nanoTime();
		for (int i = 0; i < MAX; i++) {
			threadLocalRandom.nextDouble();
		}
		long end = System.nanoTime() - start;
		System.out.println("use time1 : " + end);

		long start2 = System.nanoTime();
		for (int i = 0; i < MAX; i++) {
			Math.random();
		}
		long end2 = System.nanoTime() - start2;
		System.out.println("use time2 : " + end2);

		Assert.assertTrue(end2 > end);
	}

	// 判断两个线程之间所引用的ThreadLocalRandom实例是不一样的
	@Test
	public void testInstance() {
		final ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
		final List<ThreadLocalRandom> randomList = new ArrayList<ThreadLocalRandom>();
		// CountDownLatch的用法
		final Phaser barrier = new Phaser(1);
		
		new Thread() {
			@Override
			public void run() {
				randomList.add(ThreadLocalRandom.current());
				barrier.arrive();
			}
		}.start();

		barrier.awaitAdvance(barrier.getPhase());
		if (randomList.isEmpty()) {
			throw new NullPointerException();
		}

		Assert.assertTrue(threadLocalRandom != randomList.get(0));
	}
	
	@Test
	public void testHowtoUse(){
		final ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
		final int MAX = 100;
		int result = threadLocalRandom.nextInt(0, 100);
		
		Assert.assertTrue(MAX > result);
	}
}
view raw RandomTest.java hosted with ❤ by  GitHub



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!