【问题标题】:How does Java Async work ? Async method does not seem to run asynchronouslyJava 异步如何工作? Async 方法似乎没有异步运行
【发布时间】:2019-05-10 14:20:24
【问题描述】:

使用 Java 8。

我有一个 Logger 类,它在需要记录某些内容时调用 API。我意识到如果 API 配置不当,或者 API 没有响应,我的日志操作会花费大量时间。

同步记录示例:

public void debug(String message) {
    MDC.clear();
    MDC.put(SOME_KEY, "SOME_VALUE");
    super.debug(message);
    MDC.clear();
}

我能够确定问题出在此处,因为如果我只是评论所有内容并停止记录或执行任何操作,那么一切都会以应有的速度运行:

public void debug(String message) {
    //     MDC.clear();
    //     MDC.put(SOME_KEY, "SOME_VALUE");
    //     super.debug(message);
    //     MDC.clear();
}

所以我想使它成为一个异步调用,因为我不在乎它是否同步记录:

public void debug(String message) {
    CompletableFuture.runAsync(() -> {
        MDC.clear();
        MDC.put(SOME_KEY, "SOME_VALUE");
        super.debug(message);
        MDC.clear();
    });
}

但是对于我的主应用程序而言,这种异步调用在性能方面与同步调用一样糟糕。我错过了什么?

【问题讨论】:

    标签: java performance asynchronous


    【解决方案1】:

    您的问题是您没有提供执行人。这可能会导致 Java 为您提供的线程数少于您当前等待的调试调用,这意味着您仍然会遇到一些阻塞。在具有 4 个内核和 Java 8 上的超线程的 Intel Core i7-4790 上,我似乎同时运行了 7 个线程(逻辑 CPU 数量 - 1 个用于主线程)。您可以通过使用缓存线程池提供无限数量的线程来解决此问题:

    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    
    public class Test
    {
    
        public static void main(String[] args) throws InterruptedException
        {
            Executor ex = Executors.newCachedThreadPool();
            for(int i=0;i<100;i++)
            {
                CompletableFuture.runAsync(() -> {          
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        throw new IllegalStateException(e);
                    }
                    System.out.println("completed");
                },ex);          
            }
            TimeUnit.SECONDS.sleep(2);
        }
    }
    

    请参见上面的示例,该示例将“完成”打印 100 次。如果您删除ex 参数,它将打印得更少。

    但是,可能仍需要修复导致调试调用缓慢的根本原因,因为如果这是一项长时间运行的任务,这可能会填满您的记忆。

    另见:(https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html):

    所有没有显式 Executor 参数的异步方法都是 使用 ForkJoinPool.commonPool() 执行(除非它没有 支持至少两个并行级别,在这种情况下,一个新的 创建线程来运行每个任务)。 [...]

    【讨论】:

    • 会尝试,我也会解决根本原因,但我想先解决这个问题,因为如果远程日志 API 以某种方式出现故障,我不希望用户受到问题的影响几秒钟:)
    • 它成功了,非常感谢你,我本来可以在这个上寻找年龄的
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多