zoukankan      html  css  js  c++  java
  • 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输入代码模板解决。如图:

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

  • 相关阅读:
    MySQL binlog 组提交与 XA(两阶段提交)
    mydumper 安装报错处理
    安装 gcc-c++ 时报错和原有 gcc 版本冲突
    mysql / mysqld_safe / mysqld 常见错误处理
    Linux 内核日志——dmesg
    Java中的Atomic包
    JAVA NIO中的Channels和Buffers
    字节流InputStream/OutputStream
    字符输出流Writer简要概括
    字符输入流Reader简要概括
  • 原文地址:https://www.cnblogs.com/FunTester/p/15425066.html
Copyright © 2011-2022 走看看