方法一:Random
每次创建新的 Random(线程安全,但效率低,不推荐)
例如:
public String generateCode() {Random random = new Random();return String.format("%06d", random.nextInt(1000000));
}
不会发生线程安全问题,因为每个线程都有自己的 Random 对象。
但是:
- 每次都创建对象,效率较低。
- 高并发下可能因为种子接近,随机性略差(现代 JDK 已优化很多,但仍不推荐)。
全局共享一个 Random(线程安全,但性能一般)
private static final Random RANDOM = new Random();public String generateCode() {return String.format("%06d", RANDOM.nextInt(1000000)); // 0 ~ 999999
}
java.util.Random 在现代 JDK 中是线程安全的(内部使用原子操作维护种子),不会因为多线程而产生错误。
但是:
- 多线程竞争同一个
Random,性能会下降。 - 并发越高,竞争越明显。
// 避免以 0 开头
RANDOM.nextInt(900000) + 100000;// 原理:
RANDOM.nextInt(900000); // 0 ~ 899999
+100000; // 100000 ~ 999999
方法二:ThreadLocalRandom(推荐,并发性能最好)
public String generateCode() {// 验证码允许以 0 开头,比如 001234int code = ThreadLocalRandom.current().nextInt(1000000); // 0 ~ 999999return String.format("%06d", code);
}
每个线程都有自己的随机数状态,不需要加锁,没有竞争。
// 不以 0 开头
int code = ThreadLocalRandom.current().nextInt(100000, 1000000);
100000:最小值(包含)1000000:最大值(不包含)- 保证一定是 6 位数字。
推荐封装
如果是验证码生成,可以封装一个工具类:
import java.util.concurrent.ThreadLocalRandom;public class VerifyCodeUtil {/*** 生成6位数字验证码(允许前导0)*/public static String generate6Code() {return String.format("%06d",ThreadLocalRandom.current().nextInt(1_000_000));}/*** 生成6位数字(不允许前导0)*/public static int generate6Number() {return ThreadLocalRandom.current().nextInt(100000, 1000000);}public static void main(String[] args) {System.out.println(generate6Code());System.out.println(generate6Number());}
}
方法三:安全要求高,使用 SecureRandom
如果是:
- 登录验证码
- 短信验证码
- 重置密码验证码
- 支付验证码
更推荐:
private static final SecureRandom RANDOM = new SecureRandom();public String generateCode() {int code = RANDOM.nextInt(1_000_000);return String.format("%06d", code);
}
SecureRandom 使用密码学安全随机数,比 Random 和 ThreadLocalRandom 更难预测,更适合安全场景。