You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java使用多线程生成指定范围唯一随机数的问题及方案咨询

问题根因

你当前代码存在3个核心问题:

  1. join()用法错误:你先启动thread1,调用join()等待thread1完全执行结束后才启动thread2,本质是串行执行,完全没有发挥多线程的作用。
  2. 共享资源操作无同步:ArrayList本身不是线程安全的,你对List的contains()校验和remove()删除两个操作不是原子性的。即便你调整join()逻辑让多线程并发执行,也会出现两个线程同时校验到某个值存在,第一个线程删除成功后,第二个线程再执行删除时数值已经不存在,自然不会打印第二条输出,这就是你偶现缺结果的核心原因。
  3. 执行逻辑有漏洞:run()方法中仅生成一次随机数,如果值不存在就直接break退出,不会重新生成新的随机数尝试获取可用值,直接结束线程。

可行实现方案

方案1:加同步锁保证操作原子性,修复执行逻辑

给共享列表的检查、删除操作加同步锁,同时修改run()方法的逻辑,没有拿到有效随机数就循环生成直到获取成功:

public class randomThreadGen implements Runnable {
    private int lowerBound;
    private int upperBound;
    private final ArrayList<Integer> list = new ArrayList<>();
    private final Object lock = new Object();

    public randomThreadGen(int lowerb, int upperb) {
        this.lowerBound = lowerb;
        this.upperBound = upperb;
        for (int i = lowerb; i <= upperb; i++) {
            list.add(i);
        }
    }

    @Override
    public void run() {
        Integer val = null;
        // 循环直到拿到可用数值
        while (val == null) {
            int randomVal = ThreadLocalRandom.current().nextInt(lowerBound, upperBound + 1);
            // 检查+删除操作加锁,保证原子性
            synchronized (lock) {
                if (list.contains(randomVal)) {
                    list.remove(Integer.valueOf(randomVal));
                    val = randomVal;
                }
            }
        }
        System.out.println("Random value for " + Thread.currentThread().getName() + "  " + val);
        System.out.println("List values:  " + list);
    }
}

调整Main类逻辑,不要用join()串行执行,直接同时启动所有线程:

public class Main {
    public static void main(String[] args) {
        randomThreadGen randomRange = new randomThreadGen(1, 2);
        Thread thread1 = new Thread(randomRange);
        Thread thread2 = new Thread(randomRange);
        thread1.start();
        thread2.start();
    }
}

方案2:使用线程安全集合

直接用JDK提供的线程安全集合如CopyOnWriteArrayList、ConcurrentSkipListSet替代ArrayList,可以省去手动加锁的步骤,同时保证操作的线程安全性。

方案3:预洗牌队列方案(性能更优)

如果是范围N对应启动N个线程的场景,可以提前把范围内的所有数字打乱后存入线程安全队列,每个线程直接从队列取一个元素即可,不需要每次生成随机数再校验是否存在,执行效率更高:

public class randomThreadGen implements Runnable {
    private final ConcurrentLinkedQueue<Integer> queue;

    public randomThreadGen(int lowerb, int upperb) {
        List<Integer> list = IntStream.rangeClosed(lowerb, upperb)
                .boxed()
                .collect(Collectors.toList());
        Collections.shuffle(list);
        queue = new ConcurrentLinkedQueue<>(list);
    }

    @Override
    public void run() {
        Integer val = queue.poll();
        System.out.println("Random value for " + Thread.currentThread().getName() + "  " + val);
    }
}

内容的提问来源于stack exchange,提问作者tom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 14:15:02