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

如何让Java JUnit测试运行指定时长后终止且不标记失败

问题:如何在多线程循环运行指定时长后执行断言,避免Timeout导致测试失败

我有一个启动多个运行while(true)循环线程的类,想在测试运行一定时长后对其状态执行Assert断言,但现有的Timeout功能会导致测试失败,无法满足需求。

示例代码:

class RunProgram {

    private DataClass dataClass = new DataClass();
    private Thread1 thread1 = new Thread1(dataClass);

    void startThis() {
        ExecutorService pool = Executors.newFixedThreadPool(5);
        try {
            pool.execute(thread1);//Thread 1
            //... more threads with while loops
            runMainThread(dataClass);
        } finally {
            pool.shutdown();
        }
    }

    void runMainThread(DataClass data1){
        while(true){
            dataClass.setInternalDataInt(20);
            //do stuff
        }
    }

    public Thread1 getThread1(){
        return this.thread1;
    }
}

class Thread1 implements Runnable{
    private DataClass dataClass;

    Thread1(DataClass dataClass){
        this.dataClass = dataClass;
    }

    public void run() {
        dataClass.setInternalDataInt(10);
        while (true) {
            //dostuff
        }
    }

    public DataClass getDataClass(){
        return dataClass;
    }

    public void setDataClass(DataClass dataClass){
        this.dataClass = dataClass;
    }
}

class DataClass {
    private int internalDataInt;
    
    public int getInternalDataInt(){
        return this.internalDataInt;
    }
    
    public void setInternalDataInt(int internalDataInt){
        this.internalDataInt = internalDataInt;
    }
}

class Tests{
    @Test
    public void stateOfThread1() {
        RunProgram runProgram = new RunProgram();
        runProgram.startThis();
        //Run above for 100 millisecond and then end
        Assertions.assertEquals(runProgram.getThread1().getDataClass().getInternalDataInt(), 20);
    }
}

解决方案

方法1:修改线程支持中断,手动控制测试时长

核心是让循环线程响应中断信号,测试主线程等待指定时间后主动终止后台线程,再执行断言。

  1. 修改循环线程逻辑:将while(true)改为while(!Thread.currentThread().isInterrupted()),让线程能感知中断
  2. 调整RunProgram类:把runMainThread提交到线程池(避免阻塞测试主线程),新增终止线程的方法
  3. 修改测试类:启动程序后休眠指定时长,终止线程后执行断言

修改后的代码:

// 修改Thread1类
class Thread1 implements Runnable{
    private DataClass dataClass;

    Thread1(DataClass dataClass){
        this.dataClass = dataClass;
    }

    public void run() {
        dataClass.setInternalDataInt(10);
        // 改为响应中断的循环
        while (!Thread.currentThread().isInterrupted()) {
            //dostuff
        }
    }

    // 原有getter/setter不变
}

// 修改RunProgram类
class RunProgram {

    private DataClass dataClass = new DataClass();
    private Thread1 thread1 = new Thread1(dataClass);
    private ExecutorService pool;

    void startThis() {
        pool = Executors.newFixedThreadPool(5);
        try {
            pool.execute(thread1);
            // 把runMainThread提交到线程池,避免阻塞主线程
            pool.execute(() -> runMainThread(dataClass));
        } catch (Exception e) {
            pool.shutdownNow();
        }
    }

    void runMainThread(DataClass data1){
        // 改为响应中断的循环
        while(!Thread.currentThread().isInterrupted()){
            dataClass.setInternalDataInt(20);
            //do stuff
        }
    }

    public Thread1 getThread1(){
        return this.thread1;
    }

    // 新增:终止所有线程
    public void stopAllThreads() {
        if (pool != null) {
            pool.shutdownNow();
        }
    }
}

// 修改测试类
class Tests{
    @Test
    public void stateOfThread1() throws InterruptedException {
        RunProgram runProgram = new RunProgram();
        runProgram.startThis();
        
        // 等待100毫秒
        Thread.sleep(100);
        
        // 主动终止后台线程
        runProgram.stopAllThreads();
        
        // 执行断言
        Assertions.assertEquals(runProgram.getThread1().getDataClass().getInternalDataInt(), 20);
    }
}

方法2:用定时任务触发断言(无需修改原线程逻辑)

如果不想改动原线程代码,可以用定时任务在指定时间后执行断言,同时强制终止测试进程:

class Tests{
    @Test
    public void stateOfThread1() throws InterruptedException {
        RunProgram runProgram = new RunProgram();
        CountDownLatch latch = new CountDownLatch(1);
        
        // 单独线程启动程序,避免阻塞测试主线程
        new Thread(runProgram::startThis).start();
        
        // 100毫秒后执行断言并结束测试
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.schedule(() -> {
            try {
                Assertions.assertEquals(runProgram.getThread1().getDataClass().getInternalDataInt(), 20);
            } finally {
                latch.countDown();
                // 强制终止所有线程
                System.exit(0);
            }
        }, 100, TimeUnit.MILLISECONDS);
        
        latch.await();
        scheduler.shutdown();
    }
}

关键注意事项

  • 线程可见性:DataClass的internalDataInt字段需要加volatile关键字,或者改用AtomicInteger,避免断言时读取到缓存的旧值:
    class DataClass {
        private volatile int internalDataInt;
        // 原有getter/setter不变
    }
    
  • 阻塞操作的中断响应:如果线程循环内有Thread.sleep()等阻塞方法,要确保这些方法能抛出InterruptedException,否则shutdownNow()无法终止线程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 12:58:01