junit test函数下的多线程问题:守护线程和非守护线程

首先,我们知道 java中的线程分为两类: 守护线程和用户线程(非守护线程)。

用户线程即是运行在前台的线程,而守护线程顾名思义就是监控守护用户线程的线程,是为运行在前台的用户线程提供运行便利服务的,且只有在用户线程运行时才需要。比如垃圾回收线程。当VM检测到用户线程已经退出运行,仅剩守护线程时,VM会退出,因为没有被守护者,程序运行也就没有必要了。

守护线程并非只有虚拟机内部提供,用户在编写程序时也可以自己设置守护线程。用户可以用Thread的setDaemon(true)方法设置当前线程为守护线程。

一般我们创建的都是用户线程。但也有例外,比如在Junit环境中创建的多线程都会编程守护线程模式。

例子:

     @Test
    public  void threadTest(){
        final char[] chars1 = "123".toCharArray();
        final char[] chars2 = "456".toCharArray();
        new Thread(()->{
            synchronized (chars1){
                for (int i = 0; i < chars1.length; i++) {
                    System.out.println(chars1[i]);
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (chars2){
                    for (int i = 0; i < chars2.length; i++) {
                        System.out.println(chars2[i]);
                    }
                }
            }
        }).start();

        new Thread(()->{
            synchronized (chars2){
                for (int i = 0; i < chars2.length; i++) {
                    System.out.println(chars2[i]);
                }

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (chars1){
                    for (int i = 0; i < chars1.length; i++) {
                        System.out.println(chars1[i]);
                    }

                }
            }
        }).start();


    }

上面代码写了一个死锁的例子,运行后发现程序并没有循环等待状态,而是直接运行结束。是因为@test函数是不会等待子守护线程运行结束,只要@test函数执行结束,程序就立即结束。

如果换成在main函数中创建,则创建的都是用户线程,子用户线程未执行结束,则main函数线程会一直处于等待状态,这个进程也就不会结束。

 public static void main(String[] args) {
        final char[] chars1 = "123".toCharArray();
        final char[] chars2 = "456".toCharArray();
        new Thread(()->{
            synchronized (chars1){
                for (char c : chars1) {
                    System.out.println(c);
                }

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (chars2){
                    for (char c : chars2) {
                        System.out.println(c);
                    }
                }
            }


        }).start();

        new Thread(()->{
                synchronized (chars2){
                    for (char c : chars2) {
                        System.out.println(c);
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (chars1){
                        for (char c : chars1) {
                            System.out.println(c);
                        }
                    }
                }

        }).start();
    }

执行代码,会出现死锁等待现象。

注:在@test函数下,可以在test方法结尾手动添加awaitTermination(),让主线程等待子用户线程。

### ReportNG 多线程实现方法 #### 使用TestNG框架下的多线程支持特性 ReportNG本身是一个基于TestNG的报告插件,不直接提供多线程管理功能。然而,通过配置TestNG可以间接实现在ReportNG中的多线程测试执行。为了使多个测试用例能够并发运行并由ReportNG记录结果,需调整`testng.xml`文件设置。 ```xml <suite name="ParallelSuite" parallel="methods" thread-count="5"> <!-- 配置其他参数 --> </suite> ``` 上述XML片段展示了如何开启方法级别的并行执行模式以及设定最大线程数为5[^1]。 #### 继承自TestListenerAdapter监听器类 当涉及到复杂的日志需求或是希望捕获特定事件时,可以通过扩展`TestListenerAdapter`来自定义行为。尽管这不是专门针对多线程设计的部分,但在多线程环境下合理利用这些已有的接口有助于更好地管理监控各个线程内的活动状态变化情况。 对于每一个可能被不同线程触发的状态改变(如开始/结束某个测试),都可以重写相应的方法以便于加入额外的日志记录或其他必要的操作: ```java public class CustomizedTestListener extends TestListenerAdapter { @Override public void onTestStart(ITestResult tr){ System.out.println(Thread.currentThread().getName() + " started test: "+tr.getName()); super.onTestStart(tr); } @Override public void onTestSuccess(ITestResult tr){ System.out.println(Thread.currentThread().getName()+" finished successfully test:"+tr.getName()); super.onTestSuccess(tr); } } ``` 这段Java代码实现了对每个测试案例启动成功的跟踪,并打印当前线程名称以区分不同的工作单元。 #### 数据驱动与异常处理机制相结合 考虑到数据驱动测试可能会引入更多不确定因素,特别是在涉及资源竞争的情况下,则应考虑采用更稳健的方式来进行错误检测。例如,使用JUnit 5中的`assertThrows()`函数来确保即使是在高负载条件下也能捕捉到预期之外的行为: ```java @Test(dataProvider = "dataProviderName", expectedExceptions = IllegalArgumentException.class) public void testDataDrivenWithExceptionHandling(int input, String expectedResult) throws Exception{ Assertions.assertThrows(IllegalArgumentException.class, ()->{ // 测试逻辑... }); } ``` 这里展示了一个结合了数据供给者(`@DataProvider`)特性的例子,其中还包含了对外部抛出法参数异常的期望声明[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值