前面我們提到線程池處理批量接口請求實踐但是在語法上比較復雜,還需要進行線程間的同步,也需要一定的Java知識,最近在學習Golang語言時,感覺go關鍵字十分高效,只要是想異步執(zhí)行的方法,只需在前面添加go關鍵字即可。
如果Java也能實現(xiàn)一個類似go的關鍵字,那該多好??!
思路
Java本身也是支持閉包的,通過閉包重建一個java.lang.Runnable的匿名實現(xiàn)類,然后創(chuàng)建線程去執(zhí)行對應的方法,應該是可以實現(xiàn)簡單異步功能。
既然Java都能支持,那Groovy肯定也有解決方案了,至少可以直接用Java的方案。
幾種語言的閉包使用可以參考
實踐
思路比較簡單,下面分享一下實踐過程。
Java
不同于其他語言,Java閉包方法根據(jù)不同的參數(shù)、返回值有不同的實現(xiàn)類。這個地方有點煩,不夠靈活。我試了java.util.function下面的多個實現(xiàn)類,最終選擇了java.util.function.Supplier,原因是這個實現(xiàn)類沒有參數(shù),但是需要一個返回值。
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/,系統(tǒng)編碼格式:UTF-8,系統(tǒng)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/,系統(tǒng)編碼格式:UTF-8,系統(tǒng)Mac OS X版本:10.16
INFO-> main FunTester
INFO-> Thread-1 tester
Process finished with exit code 0
可以看出Groovy語法是非常簡單的,已經(jīng)非常接近Golang語言go關鍵字的體驗了,僅僅從語法上,性能上的問題以后會再討論。
線程池升級
因為Golang語言有自己的goroutine管理器,加上Golang特性,所以不是用很擔心創(chuàng)建過多的協(xié)程消耗更多資源。但是Java還是要考慮一下的,為了解決測試過程中創(chuàng)建過多線程導致異常出現(xiàn),我用線程池解決這個問題。通過將閉包中的方法包裝成java.lang.Runnable對象,丟給線程池去執(zhí)行。
封裝方法如下:
/**
* 異步執(zhí)行某個代碼塊
* Java調(diào)用需要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()
}
}
優(yōu)先于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/,系統(tǒng)編碼格式:UTF-8,系統(tǒng)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來滿足需求。封裝方法如下:
/**
* 異步執(zhí)行代碼塊,使用{@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("執(zhí)行異步方法時發(fā)生錯誤!", e);
} finally {
phaser.arriveAndDeregister();
}
}
};
ThreadPoolUtil.executeSync(runnable);
}