Java自定義異步功能實(shí)踐

前面我們提到線程池處理批量接口請(qǐng)求實(shí)踐但是在語(yǔ)法上比較復(fù)雜,還需要進(jìn)行線程間的同步,也需要一定的Java知識(shí),最近在學(xué)習(xí)Golang語(yǔ)言時(shí),感覺(jué)go關(guān)鍵字十分高效,只要是想異步執(zhí)行的方法,只需在前面添加go關(guān)鍵字即可。

如果Java也能實(shí)現(xiàn)一個(gè)類似go的關(guān)鍵字,那該多好啊!

思路

Java本身也是支持閉包的,通過(guò)閉包重建一個(gè)java.lang.Runnable的匿名實(shí)現(xiàn)類,然后創(chuàng)建線程去執(zhí)行對(duì)應(yīng)的方法,應(yīng)該是可以實(shí)現(xiàn)簡(jiǎn)單異步功能。

既然Java都能支持,那Groovy肯定也有解決方案了,至少可以直接用Java的方案。

幾種語(yǔ)言的閉包使用可以參考

實(shí)踐

思路比較簡(jiǎn)單,下面分享一下實(shí)踐過(guò)程。

Java

不同于其他語(yǔ)言,Java閉包方法根據(jù)不同的參數(shù)、返回值有不同的實(shí)現(xiàn)類。這個(gè)地方有點(diǎn)煩,不夠靈活。我試了java.util.function下面的多個(gè)實(shí)現(xiàn)類,最終選擇了java.util.function.Supplier,原因是這個(gè)實(shí)現(xiàn)類沒(méi)有參數(shù),但是需要一個(gè)返回值。

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

}

控制臺(tái)輸出:

INFO-> main 當(dāng)前用戶: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語(yǔ)法相當(dāng)簡(jiǎn)單,用一個(gè)groovy.lang.Closure解決所有問(wèn)題。

代碼和使用方式如下:

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

控制臺(tái)輸出:

INFO-> main 當(dāng)前用戶: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語(yǔ)法是非常簡(jiǎn)單的,已經(jīng)非常接近Golang語(yǔ)言go關(guān)鍵字的體驗(yàn)了,僅僅從語(yǔ)法上,性能上的問(wèn)題以后會(huì)再討論。

線程池升級(jí)

因?yàn)镚olang語(yǔ)言有自己的goroutine管理器,加上Golang特性,所以不是用很擔(dān)心創(chuàng)建過(guò)多的協(xié)程消耗更多資源。但是Java還是要考慮一下的,為了解決測(cè)試過(guò)程中創(chuàng)建過(guò)多線程導(dǎo)致異常出現(xiàn),我用線程池解決這個(gè)問(wèn)題。通過(guò)將閉包中的方法包裝成java.lang.Runnable對(duì)象,丟給線程池去執(zhí)行。

封裝方法如下:

    /**
     * 異步執(zhí)行某個(gè)代碼塊
     * Java調(diào)用需要return,Groovy也不需要,語(yǔ)法兼容
     *
     * @param f
     */
    public static void fun(Supplier f) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                f.get();
            }
        };
        ThreadPoolUtil.executeSync(runnable);
    }

這里我選用了Java的語(yǔ)法,為什么不用Groovy的方法封裝呢?因?yàn)镚roovy完全兼容了Java的語(yǔ)法而不失去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語(yǔ)法,需要多寫一些code,這個(gè)我目前解決方案是通過(guò)Intellij自帶的Live Templates輸入代碼模板解決。如圖:

image

下面演示一下Groovy如何使用:

    public static void main(String[] args) {

        fun {
            sleep(0.2)
            output(320)
        }
        output("FunTester")
        ThreadPoolUtil.shutFun()    
    }

同樣我們可以使用Intellij自帶的Live Templates輸入代碼模板,不再展示了。

簡(jiǎn)直如絲般順滑。

控制臺(tái)輸出:

INFO-> main 當(dāng)前用戶: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}對(duì)象
     * @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來(lái)滿足需求。封裝方法如下:

    /**
     * 異步執(zhí)行代碼塊,使用{@link Phaser}進(jìn)行多線程同步
     *
     * @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í)行異步方法時(shí)發(fā)生錯(cuò)誤!", e);
                } finally {
                    phaser.arriveAndDeregister();
                }
                
            }
        };
        ThreadPoolUtil.executeSync(runnable);
    }

Have Fun ~ Tester !

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容