Java随机抽奖系统核心是业务规则下的可控随机,需兼顾公平性、可重现性及工程实践:优选ThreadLocalRandom或带种子Random;去重用Collections.shuffle;加权抽奖用前缀和+二分查找;全程记录种子与日志确保可审计。
Java随机抽奖系统核心在于公平性与可重现性,关键不是“越随机越好”,而是“在业务规则下可控地随机”。直接用 Math.random() 或 Random 类容易忽略重复、权重、去重、线程安全等实际问题。
Math.random() 返回 double 值 [0.0, 1.0),看似简单,但有三个隐患:精度误差导致边界偏差、单例共享状态易被干扰、无法指定种子不利于测试。生产环境推荐使用 ThreadLocalRandom(多线程安全)或带种子的 Random 实例(便于复现抽奖结果)。
ThreadLocalRandom.current().nextInt(1, 101)
new Random(12345L),相同种子生成相同序列从 100 人中抽 5 个不重复中奖者,常见错误是“抽一个查一次是否已中”,在中奖率高或池子小时效率骤降。正确做法是先构建候选人列表,再用 Fisher-Yates 洗牌算法(Collections.shuffle() 内部实现)打乱,取前 N 个。
Collections.shuffle(list, new Random(seed))

list.subList(0, prizeCount),O(1) 截取,无重复校验开销当不同用户中奖概率不同(如VIP权重2、普通用户权重1),不能简单把用户重复添加进列表(浪费内存且难维护)。标准解法是预计算权重前缀和数组,生成随机值后二分查找落点。
Arrays.binarySearch(),注意处理负返回值转为插入点TreeMap 维护累积概率,支持 O(log n) 更新与查询抽奖结果必须可验证、可追溯。每次抽奖应生成唯一活动ID,并将随机种子、参与人数、中奖名单、时间戳写入日志或数据库。前端不传“抽几次”,而由后端根据配置决定轮次,避免客户端篡改请求参数。
不复杂但容易忽略。真正稳定的抽奖系统,90% 功夫花在边界控制、数据一致性和可验证设计上,而不是追求“更随机”。