Java自定义异步功能实践

前面我们提到线程池处理批量接口请求实践但是在语法上比较复杂,还需要进行线程间的同步,也需要一定的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输入代码模板解决。如图:

image

下面演示一下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 !

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容