太长不看版本
JUnit@Test的测试 不支持多线程,代码执行完就会直接退出,不会检测子线程是否结束
Junit 测试的入口TestRunner的main方法,在执行aTestRunner.start()之后,不会去判断子线程是否结束,而是调用wasSuccessful()进行判断:然后主线程接下来就会执行System.exit() 进行退出。
public synchronized boolean wasSuccessful() {
return failureCount() == 0 && errorCount() == 0;
}
解决方法 一 : 放在main函数中执行肯定没问题,因为main函数就是用户线程默认创建出来的子线程也是用户线程,jvm会等待所有用户线程执行完毕以后才会退出jvm,但是不会等守护线程(GC就是守护线程)
解决方法 二 : thread.join() 使用该方法通知主线程等待子线程执行完毕以后再结束
解决方法 三 : 同理给主线程添加耗时操作,Thread.sleep(1000);
解决方法 四 : 使用测试方法中CountDownLatch类,来等其他线程执行完再执行主方法中的类容
解决方法 五 : 使用executor.awaitTermination()
解决方法 六: 使用扩展测试类,如GroboUtils
1. 问题
最近在学习RabbitMQ的相关内容,在做练习的时候发现了一个问题,basicConsume 在Junit 的@Test 会直接结束,不会去监听消息。
生产者 发送消息
public class Provider {
//生产消息的代码
@Test
public void testSendMessage() throws IOException, TimeoutException, InterruptedException {
//创建连接mq的连接工厂
var connectionFactory = new ConnectionFactory();
//设置连接rabbitmq的主机
connectionFactory.setHost("127.0.0.1");
//设置端口号
connectionFactory.setPort(5672);
//设置连接的虚拟主机
connectionFactory.setVirtualHost("/ems");
//设置访问虚拟主机的用户名密码
connectionFactory.setUsername("ems");
connectionFactory.setPassword("123");
//获取连接对象
var connection = connectionFactory.newConnection();
//获取连接通道对象
var channel = connection.createChannel();
//通道绑定消息队列
//参数1: 队列名称 不存在自动创建
//参数2: durable 队列的对象是否要持久化
//参数3: exclusive 是否独占队列 代表当前队列是否当前连接可用
//参数4: autoDelete 是否在消费完成后自动删除队列
//参数5: 额外参数
channel.queueDeclare("hello", false, false, false, null);
//发布消息
//参数1: 交换机名称
//参数2: 队列名称
//参数3: 传递消息额外设置
//参数4: 消息的具体内容
var scanner = new Scanner(System.in);
while (scanner.hasNext()) {
channel.basicPublish("", "hello", null,scanner.nextLine().getBytes());
}
//关闭连接
channel.close();
}
}
消费者 接收消息
public class Customer {
@Test
public void testAcceptMsg() throws IOException, TimeoutException, InterruptedException {
//创建连接mq的连接工厂
var connectionFactory = new ConnectionFactory();
//设置连接rabbitmq的主机
connectionFactory.setHost("127.0.0.1");
//设置端口号
connectionFactory.setPort(5672);
//设置连接的虚拟主机
connectionFactory.setVirtualHost("/ems");
//设置访问虚拟主机的用户名密码
connectionFactory.setUsername("ems");
connectionFactory.setPassword("123");
//创建连接对象
var connection = connectionFactory.newConnection();
//创建通道
var channel = connection.createChannel();
//通道绑定队列
channel.queueDeclare("hello", false, false, false, null);
//消费消息
//参数1:消费哪个队列
//参数2:开启消息的自动确认机制
//参数3:消费消息时候的回调接口
channel.basicConsume("hello", true, new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("String.valueOf(body) = " + new String(body));
}
});
// 需要一直监听队列进行处理
}
消费者会直接结束
2. 分析
2.1 channel.basicConsume
会以子线程的方式去处理
2.2 可以使用 jvm jstack工具观察
没有消息
消费一条消息
消费多条消息
2.2 Junit @Test
可以阅读 守护线程和非守护线程
Junit 测试的入口TestRunner的main方法,在执行aTestRunner.start()之后,不会去判断子线程是否结束,而是调用wasSuccessful()进行判断:然后主线程接下来就会执行System.exit() 进行退出。
public synchronized boolean wasSuccessful() {
return failureCount() == 0 && errorCount() == 0;
}
3. 解决方案:
可以保证主线程不结束就可以,等待子线程全部运行结束后在结束主线程。
解决方法 一 : 放在main函数中执行肯定没问题,因为main函数就是用户线程默认创建出来的子线程也是用户线程,jvm会等待所有用户线程执行完毕以后才会退出jvm,但是不会等守护线程(GC就是守护线程)
解决方法 二 : thread.join() 使用该方法通知主线程等待子线程执行完毕以后再结束
解决方法 三 : 同理给主线程添加耗时操作,Thread.sleep(1000);
解决方法 四 : 使用测试方法中CountDownLatch类,来等其他线程执行完再执行主方法中的类容
解决方法 五 : 使用executor.awaitTermination()
解决方法 六: 使用扩展测试类,如GroboUtils