Usando Java 8.
Eu tenho uma classe Logger que chama um API sempre que ele precisa para log algo. Percebi que, se a API é de alguma forma mal configurado, ou a API simplesmente não responder, minha ação log leva um lote awefull de tempo.
Exemplo de log síncrono
public void debug(String message) {
MDC.clear();
MDC.put(SOME_KEY, "SOME_VALUE");
super.debug(message);
MDC.clear();
}
Eu era capaz de identificar que o problema é aqui, porque se eu apenas comentário tudo e parar o registo ou fazer qualquer coisa, tudo corre tão rápido como deveria:
public void debug(String message) {
// MDC.clear();
// MDC.put(SOME_KEY, "SOME_VALUE");
// super.debug(message);
// MDC.clear();
}
Então eu pensei para fazer esta uma chamada assíncrona, já que eu não me importo se ele está conectado de forma síncrona:
public void debug(String message) {
CompletableFuture.runAsync(() -> {
MDC.clear();
MDC.put(SOME_KEY, "SOME_VALUE");
super.debug(message);
MDC.clear();
});
}
Mas esta chamada assíncrona é tão ruim, o desempenho sábio para meu aplicativo principal, como a chamada síncrona. O que estou perdendo ?
Seu problema é que você não fornecer um executor. Isto pode causar Java para fornecê-lo com menos fios do que você tem atualmente aguardando chamadas de depuração, o que significa que você ainda obter algum bloqueio. No meu Intel Core i7-4790 com 4 núcleos e hyperthreading em Java 8, eu parecem ter 7 threads em execução ao mesmo tempo (número de CPUs lógicas - 1 para o segmento principal). Você pode corrigir isso através do fornecimento de um número ilimitado de tópicos usando um pool de threads em cache:
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);
}
}
Veja o exemplo acima, que imprime "concluído" 100 vezes. Se você remover o ex
parâmetro, ele irá imprimir muito menos.
No entanto, a causa subjacente das chamadas de depuração lentos ainda pode precisar de ser corrigido, pois isso pode preencher a sua memória se é uma tarefa longa em execução.
Veja também: ( https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html ):
Todos os métodos assíncronos sem um argumento Executor explícito são realizadas utilizando o ForkJoinPool.commonPool () (a menos que ele não suporta um nível de paralelismo de pelo menos dois, nesse caso, um novo segmento é criado para executar cada tarefa). [...]