在上期Java自定义异步功能实践文章中,我设计了一个关键字,传入一个闭包,然后异步执行闭包中的代码块。但是在实际工作中情况又更复杂了一些。因为在创建执行异步方法的线程池时候,遇到了一些问题。
如何创建线程池 core 数值大于 1,就必须手动关闭线程池
如果创建线程池 core=0,那么必须设置一个不为零的 workQueue
如果 workQueue 设置太小,无法容纳更多任务
如果 workQueue 设置太大,无法新建更多线程(实际中只有 1 个线程被创建)
经过一些人生的思考,我觉定使用守护进程来解决这个问题。参考创建Java守护线程。
思路
执行异步方法的线程池,我使用定长线程池,设置线程数 16,因为这个场景主要是在批量执行脚本使用,所以效率优先。设置 workQueue 为 1 百万(或者 10 万),目前使用中没有差别。
如何在测试结束之后,利用守护进程的特性,等待 main 线程执行结束,然后回收资源。
为了避免浪费,只在使用异步功能时再启用这个守护进程。
分步实现
创建线程池
方法如下:
private static volatile ExecutorService funPool; /** * 获取异步任务连接池 * @return */ static ExecutorService getFunPool() { if (funPool == null) { synchronized (ThreadPoolUtil.class) { if (funPool == null) { funPool = createFixedPool(Constant.POOL_SIZE); daemon() } } } return funPool }
复制代码
创建守护线程
/** * 执行daemon线程,保障main方法结束后关闭线程池 * @return */ static boolean daemon() { def thread = new Thread(new Runnable() {
@Override void run() { while (checkMain()) { SourceCode.sleep(1.0) } ThreadPoolUtil.shutFun() } }) thread.setDaemon(true) thread.setName("FT-D") thread.start() logger.info("守护线程:{}开启!", thread.getName()) }
复制代码
检查 main 线程是否存活
/** * 检查main线程是否存活 * @return */ static boolean checkMain() { def count = Thread.activeCount() def group = Thread.currentThread().getThreadGroup() def threads = new Thread[count] group.enumerate(threads) for (i in 0..<count) { if (threads[i].getName() == "main") return true } false }
复制代码
测试
测试脚本
简单使用了 Groovy 语法糖中times语法,含义就是从 0~20 遍历闭包内容,it 表示遍历索引,从 0 开始到 19。
public static void main(String[] args) { 20.times { def a = it as String fun{ sleep(1.0) output(StringUtil.right("index:" + a, 10) + Time.getNow()) } }
}
复制代码
下面写个 Java 版本的比较容易理解:
public static void main(String[] args) { for (int i = 0; i < 20; i++) { Integer a = i; fun(() -> { sleep(1.0); output(StringUtil.right("index:" + a, 10) + Time.getNow()); return null; }); } }
复制代码
控制台输出
INFO-> main 当前用户:oker,工作目录:/Users/oker/IdeaProjects/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16INFO-> main 守护线程:FT-D开启!INFO-> FT-13 index:12 20211011182658INFO-> FT-3 index:2 20211011182658INFO-> FT-16 index:15 20211011182658INFO-> FT-14 index:13 20211011182658INFO-> FT-5 index:4 20211011182658INFO-> FT-4 index:3 20211011182658INFO-> FT-12 index:11 20211011182658WARN-> FT-D 异步线程池关闭!INFO-> FT-10 index:9 20211011182658INFO-> FT-7 index:6 20211011182658INFO-> FT-2 index:1 20211011182658INFO-> FT-11 index:10 20211011182658INFO-> FT-8 index:7 20211011182658INFO-> FT-15 index:14 20211011182658INFO-> FT-1 index:0 20211011182658INFO-> FT-9 index:8 20211011182658INFO-> FT-6 index:5 20211011182658INFO-> FT-16 index:16 20211011182659INFO-> FT-7 index:19 20211011182659INFO-> FT-5 index:17 20211011182659INFO-> FT-12 index:18 20211011182659
Process finished with exit code 0
复制代码
线程同步
多线程同步依然使用java.util.concurrent.Phaser类,不过加上这个参数后有点破坏原来优雅的语法。
Groovy 版本:
public static void main(String[] args) { def phaser = new Phaser(1) 20.times { def a = it as String fun { sleep(1.0) output(StringUtil.right("index:" + a, 10) + Time.getNow()) } , phaser } phaser.arriveAndAwaitAdvance() }
复制代码
这么写还是非常舒服的,不过编译器会报错,请忽略,编译器也不一定都是正确的。
Java 版本:
public static void main(String[] args) { Phaser phaser = new Phaser(1); for (int i = 0; i < 20; i++) { Integer a = i; fun(() -> { sleep(1.0); output(StringUtil.right("index:" + a, 10) + Time.getNow()); return null; },phaser); } phaser.arriveAndAwaitAdvance(); }
复制代码
控制台输出:
INFO-> main 当前用户:oker,工作目录:/Users/oker/IdeaProjects/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16INFO-> main 守护线程:FT-D开启!INFO-> FT-11 index:10 20211011185814INFO-> FT-1 index:0 20211011185814INFO-> FT-5 index:4 20211011185814INFO-> FT-3 index:2 20211011185814INFO-> FT-16 index:15 20211011185814INFO-> FT-10 index:9 20211011185814INFO-> FT-7 index:6 20211011185814INFO-> FT-14 index:13 20211011185814INFO-> FT-9 index:8 20211011185814INFO-> FT-12 index:11 20211011185814INFO-> FT-15 index:14 20211011185814INFO-> FT-8 index:7 20211011185814INFO-> FT-6 index:5 20211011185814INFO-> FT-13 index:12 20211011185814INFO-> FT-2 index:1 20211011185814INFO-> FT-4 index:3 20211011185814INFO-> FT-3 index:16 20211011185815INFO-> FT-15 index:19 20211011185815INFO-> FT-7 index:18 20211011185815INFO-> FT-16 index:17 20211011185815WARN-> FT-D 异步线程池关闭!
Process finished with exit code 0
复制代码
可以看到WARN-> FT-D 异步线程池关闭!是最后打印的,符合预期。
Have Fun ~ Tester !
评论