7.3.5 Java7的ThreadLocalRandom与Random

7.3.5 Java7的ThreadLocalRandom与Randon

Random类专门用于生成一个伪随机数,它有两个构造器:

  1. 一个构造器使用默认的种子(以当前时间作为种子),
  2. 另一个构造器需要程序员显式传入一个long型整数的种子

ThreadLocalRandom类是Java7新增的一个类,它是Random的增强版。在并发访问的环境下,使用ThreadLocalRandom来代替Random可以减少多线程资源竞争,最终保证系统具有更好的线程安全性。
ThreadLocalRandom类的用法与Random类的用法基本相似,它提供了一个静态current()方法来获取ThreadLocalRandom对象,获取该对象之后即可调用各种nextXxx()方法来获取伪随机数了。

ThreadLocalRandomRandom都比Mathrandom方法提供了更多的方式来生成各种伪随机数:

  • 可以生成浮点类型的伪随机数,
  • 也可以生成整数类型的伪随机数,
  • 还可以指定生成随机数的范围。

Random类实例

关于Random类的用法如下程序所示。

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
import java.util.*;

public class RandomTest {
public static void main(String[] args) {
// 使用当前时间作为种子
Random rand = new Random();
// 生成随机的boolean值
System.out.println("rand.nextBoolean():" + rand.nextBoolean());

byte[] buffer = new byte[16];
rand.nextBytes(buffer);
System.out.println("rand.nextBytes(buffer):"+Arrays.toString(buffer));

// 生成0.0~1.0之间的伪随机double数
System.out.println("rand.nextDouble():" + rand.nextDouble());

// 生成0.0~1.0之间的伪随机float数
System.out.println("rand.nextFloat():" + rand.nextFloat());

// 生成平均值是 0.0,标准差是 1.0的 伪高斯数
System.out.println("rand.nextGaussian():" + rand.nextGaussian());

// 生成一个处于int整数取值范围的 伪随机整数
System.out.println("rand.nextInt():" + rand.nextInt());

// 生成0~26之间的伪随机整数
System.out.println("rand.nextInt(26):" + rand.nextInt(26));

// 生成一个处于long整数取值范围的伪随机整数
System.out.println("rand.nextLong():" + rand.nextLong());
}
}

运行效果1:

1
2
3
4
5
6
7
8
rand.nextBoolean():true
rand.nextBytes(buffer):[-6, 11, 108, 120, 126, -13, 84, -56, 45, 114, -62, -98, 113, 85, 89, 113]
rand.nextDouble():0.8120795682241748
rand.nextFloat():0.5804569
rand.nextGaussian():-1.0469886892688502
rand.nextInt():589045688
rand.nextInt(26):2
rand.nextLong():-474418649996311808

运行效果2:

1
2
3
4
5
6
7
8
rand.nextBoolean():true
rand.nextBytes(buffer):[31, -120, -74, -13, 32, -55, 3, 0, 39, -47, -122, -61, 113, -113, -45, 102]
rand.nextDouble():0.6114733221402615
rand.nextFloat():0.4678058
rand.nextGaussian():-1.1066057114192767
rand.nextInt():1796032384
rand.nextInt(26):13
rand.nextLong():5259734743396927359

Random类如何获取大于等于N小于等于M之间的随机数

RandomnextInt方法只能传入一个参数,只能获取大于等于0小于N之间的随机整数.如果要获取大于等于N,小于M的随机整数需要通过数学计算来完成.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.util.Random;

public class RandomIntN2MTest {

public static void main(String[] args) {
Random random = new Random();
int times = 20;
for (int i = 0; i < times; i++) {
// 生成[0,2)之间的随机整数,也就是[0,1]之间的随机整数
System.out.print(random.nextInt(2) + ",");
}
System.out.println();
int min = 2, max = 4;
for (int i = 0; i < times; i++) {
// 生成[min,max)之间的随机正整数
System.out.print((random.nextInt(max - min) + min) + ",");
}
System.out.println();
for (int i = 0; i < times; i++) {
// 生成[min,max]之间的随机正整数
System.out.print((random.nextInt(max - min + 1) + min) + ",");
}
}
}
1
2
3
0,1,0,0,0,0,1,0,0,0,1,1,1,1,0,1,0,1,1,0,
3,3,3,3,3,3,2,3,3,2,3,2,3,3,3,2,3,3,3,3,
3,4,4,2,3,2,2,2,3,3,4,2,2,2,3,4,2,4,4,2,

Random对象的种子相同则生成的随机数也相同

Random使用一个48位的种子,如果这个类的两个实例是用同一个种子创建的,对它们以同样的顺序调用方法,则它们会产生相同的数字序列。

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

import java.util.*;

public class SeedTest {
public static void main(String[] args) {
Random r1 = new Random(50);
System.out.println("第一个种子为50的Random对象");
System.out.println("r1.nextBoolean():\t" + r1.nextBoolean());
System.out.println("r1.nextInt():\t\t" + r1.nextInt());
System.out.println("r1.nextDouble():\t" + r1.nextDouble());
System.out.println("r1.nextGaussian():\t" + r1.nextGaussian());
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%%");

Random r2 = new Random(50);
System.out.println("第二个种子为50的Random对象");
System.out.println("r2.nextBoolean():\t" + r2.nextBoolean());
System.out.println("r2.nextInt():\t\t" + r2.nextInt());
System.out.println("r2.nextDouble():\t" + r2.nextDouble());
System.out.println("r2.nextGaussian():\t" + r2.nextGaussian());
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%%");

Random r3 = new Random(100);
System.out.println("种子为100的Random对象");
System.out.println("r3.nextBoolean():\t" + r3.nextBoolean());
System.out.println("r3.nextInt():\t\t" + r3.nextInt());
System.out.println("r3.nextDouble():\t" + r3.nextDouble());
System.out.println("r3.nextGaussian():\t" + r3.nextGaussian());
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
}
}

运行上面程序,看到如下结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
第一个种子为50的Random对象
r1.nextBoolean(): true
r1.nextInt(): -1727040520
r1.nextDouble(): 0.6141579720626675
r1.nextGaussian(): 2.377650302287946
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
第二个种子为50的Random对象
r2.nextBoolean(): true
r2.nextInt(): -1727040520
r2.nextDouble(): 0.6141579720626675
r2.nextGaussian(): 2.377650302287946
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
种子为100的Random对象
r3.nextBoolean(): true
r3.nextInt(): -1139614796
r3.nextDouble(): 0.19497605734770518
r3.nextGaussian(): 0.6762208162903859
%%%%%%%%%%%%%%%%%%%%%%%%%%%%

种子相同随机数相同

只要两个Randon对象的种子相同,而且方法的调用顺序也相同,它们就会产生相同的数字序列
也就是说, Random产生的数字并不是真正随机的,而是一种伪随机。
为了避免两个Random对象产生相同的数字序列,通常推荐使用当前时间作为Random对象的种子

如下代码所示:
Random rand=new Random(System.currentTimeMillis());
或者直接Random rand=new Random(),因为系统会默认使用当前时间作为种子.

ThreadLocalRandom

在多线程环境下使用ThreadLocalRandom的方式与使用Random基本类似

如下程序片段示范了ThreadLocalRandom的用法。

1
2
3
ThreadLocalRandom rand=ThreadLocalRandom.current();
// 获取[4,20)之间的随机整数
int vall=rand.nextInt(4,20);

ThreadLocalRandom的nextInt​

ThreadLocalRandomnextInt方法比Random提供的nextInt要强,
ThreadLocalRandom来提供了nextInt(int min, int max)方法来获取大于等于min,小于max的方法.通过ThreadLocalRandom的这个方法我们可以很方便的获取大于等于N小于等于M之间的随机正整数.