前面我们提到线程池处理批量接口请求实践但是在语法上比较复杂,还需要进行线程间的同步,也需要一定的 Java 知识,最近在学习 Golang 语言时,感觉 go 关键字十分高效,只要是想异步执行的方法,只需在前面添加 go 关键字即可。
如果 Java 也能实现一个类似 go 的关键字,那该多好啊!
思路
Java 本身也是支持闭包的,通过闭包重建一个java.lang.Runnable
的匿名实现类,然后创建线程去执行对应的方法,应该是可以实现简单异步功能。
既然 Java 都能支持,那 Groovy 肯定也有解决方案了,至少可以直接用 Java 的方案。
几种语言的闭包使用可以参考
实践
思路比较简单,下面分享一下实践过程。
Java
不同于其他语言,Java 闭包方法根据不同的参数、返回值有不同的实现类。这个地方有点烦,不够灵活。我试了java.util.function
下面的多个实现类,最终选择了java.util.function.Supplier
,原因是这个实现类没有参数,但是需要一个返回值。
There is no requirement that a new or distinct result be returned each time the supplier is invoked.
代码和使用方式如下:
package com.funtest.javatest;
import com.funtester.frame.SourceCode;
import java.util.function.Supplier;
public class Sync extends SourceCode {
public static void main(String[] args) {
sync(() -> {
sleep(0.1);
output("tester");
return DEFAULT_CHARSET;
});
output("FunTester");
}
public static void sync(Supplier f) {
new Thread(() -> {
f.get();
}).start();
}
}
复制代码
控制台输出:
INFO-> main 当前用户:oker,工作目录:/Users/oker/IdeaProjects/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> main FunTester
INFO-> Thread-1 tester
Process finished with exit code 0
复制代码
这里先不展示 Groovy 如何使用了。后面有更精彩的。
Groovy
Groovy 语法相当简单,用一个groovy.lang.Closure
解决所有问题。
代码和使用方式如下:
import com.funtester.frame.SourceCode
class Sync extends SourceCode {
public static void main(String[] args) {
sync {
sleep(0.2)
output(320)
}
output("FunTester")
}
static void sync(Closure f) {
new Thread(f()).start()
}
}
复制代码
控制台输出:
INFO-> main 当前用户:oker,工作目录:/Users/oker/IdeaProjects/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> main FunTester
INFO-> Thread-1 tester
Process finished with exit code 0
复制代码
可以看出 Groovy 语法是非常简单的,已经非常接近 Golang 语言 go 关键字的体验了,仅仅从语法上,性能上的问题以后会再讨论。
线程池升级
因为 Golang 语言有自己的 goroutine 管理器,加上 Golang 特性,所以不是用很担心创建过多的协程消耗更多资源。但是 Java 还是要考虑一下的,为了解决测试过程中创建过多线程导致异常出现,我用线程池解决这个问题。通过将闭包中的方法包装成java.lang.Runnable
对象,丢给线程池去执行。
封装方法如下:
/**
* 异步执行某个代码块
* Java调用需要return,Groovy也不需要,语法兼容
*
* @param f
*/
public static void fun(Supplier f) {
Runnable runnable = new Runnable() {
@Override
public void run() {
f.get();
}
};
ThreadPoolUtil.executeSync(runnable);
}
复制代码
这里我选用了 Java 的语法,为什么不用 Groovy 的方法封装呢?因为 Groovy 完全兼容了 Java 的语法而不失去 Groovy 自己的特性。
下面演示一下 Java 如何使用:
public class FunT extends FunLibrary {
public static void main(String[] args) {
fun(()->{
sleep(0.1);
output("FunTester");
return null;
});
output("fds");
ThreadPoolUtil.shutFun()
}
}
复制代码
优先于 Java 语法,需要多写一些 code,这个我目前解决方案是通过Intellij
自带的Live Templates
输入代码模板解决。如图:
下面演示一下 Groovy 如何使用:
public static void main(String[] args) {
fun {
sleep(0.2)
output(320)
}
output("FunTester")
ThreadPoolUtil.shutFun()
}
复制代码
同样我们可以使用Intellij
自带的Live Templates
输入代码模板,不再展示了。
简直如丝般顺滑。
控制台输出:
INFO-> main 当前用户:oker,工作目录:/Users/oker/IdeaProjects/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> main FunTester
INFO-> FT-1 320
Process finished with exit code 0
复制代码
这里我自定义了线程的名字,方法如下:
/**
* 自定义{@link ThreadFactory}对象
* @return
*/
static ThreadFactory getFactory() {
if (FunFactory == null) {
synchronized (ThreadPoolUtil.class) {
if (FunFactory == null) {
FunFactory = new ThreadFactory() {
@Override
Thread newThread(Runnable runnable) {
Thread thread = new Thread(runnable);
def increment = threadNum.getAndIncrement()
def name = increment < 10 ? "00" + increment : increment < 100 ? "0" + increment : Constant.EMPTY + increment
thread.setName("FT-" + name);
return thread;
}
}
}
}
}
return FunFactory
}
复制代码
多线程同步
如果遇到有多线程同步需求,那么依旧使用java.util.concurrent.Phaser
来满足需求。封装方法如下:
/**
* 异步执行代码块,使用{@link Phaser}进行多线程同步
*
* @param f
* @param phaser
*/
public static void fun(Supplier f, Phaser phaser) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
phaser.register();
f.get();
} catch (Exception e) {
logger.warn("执行异步方法时发生错误!", e);
} finally {
phaser.arriveAndDeregister();
}
}
};
ThreadPoolUtil.executeSync(runnable);
}
复制代码
Have Fun ~ Tester !
评论