Как работает Java Async?Асинхронный метод не работает асинхронно - PullRequest
4 голосов
/ 10 мая 2019

Использование 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();
    });
}

Но этот асинхронный вызов так же плох с точки зрения производительности для моего основного приложения, как и синхронный вызов. Чего мне не хватает?

1 Ответ

5 голосов
/ 10 мая 2019

Ваша проблема в том, что вы не предоставляете исполнителя.Это может привести к тому, что Java предоставит вам меньше потоков, чем в настоящее время ожидающие отладочные вызовы, что означает, что вы все еще получаете некоторую блокировку.На моем Intel Core i7-4790 с 4 ядрами и гиперпоточностью на Java 8 у меня, кажется, работает 7 потоков одновременно (число логических процессоров - 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 () (если только он не поддерживает уровень параллелизма по крайней мередва, и в этом случае новый поток создается для выполнения каждой задачи). [...]

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...