在今年的敏捷团队建设中,我通过Suite执行器实现了一键自动化单元测试。Juint除了Suite执行器还有哪些执行器呢?由此我的Runner探索之旅开始了!
需要说明,本文的线程池都是java.util.concurrent.ExecutorService线程池,本文将围绕验证,阅读源码俩方面来解析这个问题。
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。
2.1 验证execute提交线程池中
2.1.1 测试代码:
public class ThreadPoolExecutorDeadTest {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = buildThreadPoolExecutor();
executorService.execute(() -> exeTask("execute"));
executorService.execute(() -> exeTask("execute"));
executorService.execute(() -> exeTask("execute-exception"));
executorService.execute(() -> exeTask("execute"));
executorService.execute(() -> exeTask("execute"));
Thread.sleep(5000);
System.out.println("再次执行任务=======================");
executorService.execute(() -> exeTask("execute"));
executorService.execute(() -> exeTask("execute"));
executorService.execute(() -> exeTask("execute"));
executorService.execute(() -> exeTask("execute"));
executorService.execute(() -> exeTask("execute"));
}
public static ExecutorService buildThreadPoolExecutor() {
return new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000), new ThreadFactoryBuilder().setNameFormat("test-%s").build()
, new ThreadPoolExecutor.CallerRunsPolicy());
}
private static void exeTask(String name) {
String printStr = "[thread-name:" + Thread.currentThread().getName() + ",执行方式:" + name + "]";
if ("execute-exception".equals(name)) {
throw new RuntimeException(printStr + ", 我抛异常了");
} else {
System.out.println(printStr);
}
}
}
2.1.2 执行结果如下:
2.1.3 结论:
execute 提交到线程池的方式,如果执行中抛出异常,并且没有在执行逻辑中catch,那么会抛出异常,并且移除抛出异常的线程,创建新的线程放入到线程池中。
2.2 验证submit提交线程池中
2.2.1 测试代码:
public class ThreadPoolExecutorDeadTest {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = buildThreadPoolExecutor();
executorService.submit(() -> exeTask("execute"));
executorService.submit(() -> exeTask("execute"));
executorService.submit(() -> exeTask("execute-exception"));
executorService.submit(() -> exeTask("execute"));
executorService.submit(() -> exeTask("execute"));
Thread.sleep(5000);
System.out.println("再次执行任务=======================");
executorService.submit(() -> exeTask("execute"));
executorService.submit(() -> exeTask("execute"));
executorService.submit(() -> exeTask("execute"));
executorService.submit(() -> exeTask("execute"));
executorService.submit(() -> exeTask("execute"));
}
public static ExecutorService buildThreadPoolExecutor() {
return new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000), new ThreadFactoryBuilder().setNameFormat("test-%s").build()
, new ThreadPoolExecutor.CallerRunsPolicy());
}
private static void exeTask(String name) {
String printStr = "[thread-name:" + Thread.currentThread().getName() + ",执行方式:" + name + "]";
if ("execute-exception".equals(name)) {
throw new RuntimeException(printStr + ", 我抛异常了");
} else {
System.out.println(printStr);
}
}
}
2.2.2 执行结果如下:
2.2.3 结论:
submit 提交到线程池的方式,如果执行中抛出异常,并且没有catch,不会抛出异常,不会创建新的线程。
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。
3.1 java.util.concurrent.AbstractExecutorService#submit(java.lang.Runnable)
3.2 查看execute方法的执行逻辑java.util.concurrent.ThreadPoolExecutor#runWorker
3.3 java.util.concurrent.ThreadPoolExecutor#processWorkerExit
可以发现,如果抛出异常,会移除抛出异常的线程,创建新的线程。
3.4 为什么submit方法,没有创建新的线程,而是继续复用原线程?
还记得,我们在3.1的时候,发现submit也是调用了execute方法,但是在调用之前,包装了一层 RunnableFuture,那一定是在RunnableFuture的实现 FutureTask中有特殊处理了,我们查看源码可以发现。
但是,我们通过java.util.concurrent.FutureTask#get(),就可以获取对应的异常信息。
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。
当一个线程池里面的线程异常后:
以上俩种执行方式,都不会影响线程池里面其他线程的正常执行。