在上期[​​Java自定义异步功能实践​​文章中,我设计了一个关键字,传入一个闭包,然后异步执行闭包中的代码块。但是在实际工作中情况又更复杂了一些。因为在创建执行异步方法的线程池时候,遇到了一些问题。

  • 如何创建线程池core数值大于1,就必须手动关闭线程池
  • 如果创建线程池core=0,那么必须设置一个不为零的workQueue
  • 如果workQueue设置太小,无法容纳更多任务
  • 如果workQueue设置太大,无法新建更多线程(实际中只有1个线程被创建)

经过一些人生的思考,我觉定使用守护进程来解决这个问题。参考[​​创建Java守护线程​​。

思路

执行异步方法的线程池,我使用定长线程池,设置线程数16,因为这个场景主要是在批量执行脚本使用,所以效率优先。设置workQueue为1百万(或者10万),目前使用中没有差别。 如何在测试结束之后,利用守护进程的特性,等待main线程执行结束,然后回收资源。 为了避免浪费,只在使用异步功能时再启用这个守护进程。

分步实现

创建线程池

方法如下:

Java
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
}

创建守护线程

Java
/**
* 执行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线程是否存活

Java
/**
* 检查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..
测试

测试脚本

简单使用了Groovy语法糖中​​times​​语法,含义就是从0~20遍历闭包内容,it表示遍历索引,从0开始到19。

Java
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版本的比较容易理解:

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;
});
}
}

控制台输出

ll
INFO-> main 当前用户:oker,工作目录:/Users/oker/IdeaProjects/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> main 守护线程:FT-D开启!
INFO-> FT-13 index:12 20211011182658
INFO-> FT-3 index:2 20211011182658
INFO-> FT-16 index:15 20211011182658
INFO-> FT-14 index:13 20211011182658
INFO-> FT-5 index:4 20211011182658
INFO-> FT-4 index:3 20211011182658
INFO-> FT-12 index:11 20211011182658
WARN-> FT-D 异步线程池关闭!
INFO-> FT-10 index:9 20211011182658
INFO-> FT-7 index:6 20211011182658
INFO-> FT-2 index:1 20211011182658
INFO-> FT-11 index:10 20211011182658
INFO-> FT-8 index:7 20211011182658
INFO-> FT-15 index:14 20211011182658
INFO-> FT-1 index:0 20211011182658
INFO-> FT-9 index:8 20211011182658
INFO-> FT-6 index:5 20211011182658
INFO-> FT-16 index:16 20211011182659
INFO-> FT-7 index:19 20211011182659
INFO-> FT-5 index:17 20211011182659
INFO-> FT-12 index:18 20211011182659


Process finished with exit code 0
线程同步

多线程同步依然使用​​java.util.concurrent.Phaser​​类,不过加上这个参数后有点破坏原来优雅的语法。 Groovy版本:

Java
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版本:

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();
}

控制台输出:

ll
INFO-> main 当前用户:oker,工作目录:/Users/oker/IdeaProjects/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> main 守护线程:FT-D开启!
INFO-> FT-11 index:10 20211011185814
INFO-> FT-1 index:0 20211011185814
INFO-> FT-5 index:4 20211011185814
INFO-> FT-3 index:2 20211011185814
INFO-> FT-16 index:15 20211011185814
INFO-> FT-10 index:9 20211011185814
INFO-> FT-7 index:6 20211011185814
INFO-> FT-14 index:13 20211011185814
INFO-> FT-9 index:8 20211011185814
INFO-> FT-12 index:11 20211011185814
INFO-> FT-15 index:14 20211011185814
INFO-> FT-8 index:7 20211011185814
INFO-> FT-6 index:5 20211011185814
INFO-> FT-13 index:12 20211011185814
INFO-> FT-2 index:1 20211011185814
INFO-> FT-4 index:3 20211011185814
INFO-> FT-3 index:16 20211011185815
INFO-> FT-15 index:19 20211011185815
INFO-> FT-7 index:18 20211011185815
INFO-> FT-16 index:17 20211011185815
WARN-> FT-D 异步线程池关闭!


Process finished with exit code 0

可以看到​​WARN-> FT-D 异步线程池关闭!​​是最后打印的,符合预期。

Have Fun ~ Tester !