Asynkron programmering i Java

1. Oversigt

Med den voksende efterspørgsel efter at skrive ikke-blokerende kode har vi brug for måder at udføre koden asynkront.

I denne vejledning ser vi på et par måder at opnå asynkron programmering i Java. Vi udforsker også et par Java-biblioteker, der leverer out-of-the-box-løsninger.

2. Asynkron programmering i Java

2.1. Tråd

Vi kan oprette en ny tråd til at udføre enhver operation asynkront. Med frigivelsen af ​​lambda-udtryk i Java 8 er det renere og mere læsbart.

Lad os oprette en ny tråd, der beregner og udskriver et nummer på et nummer:

int antal = 20; Tråd newThread = ny tråd (() -> {System.out.println ("Faktor af" + nummer + "er:" + faktor (antal));}); newThread.start ();

2.2. FutureTask

Siden Java 5 er den Fremtid interface giver en måde at udføre asynkrone operationer ved hjælp af FutureTask.

Vi kan bruge Indsend metode til ExecutorService for at udføre opgaven asynkront og returnere forekomsten af FutureTask.

Så lad os finde et nummer af et nummer:

ExecutorService threadpool = Executors.newCachedThreadPool (); Future futureTask = threadpool.submit (() -> factorial (nummer)); while (! futureTask.isDone ()) {System.out.println ("FutureTask er ikke færdig endnu ..."); } langt resultat = futureTask.get (); threadpool.shutdown ();

Her har vi brugt Er gjort metode leveret af Fremtid interface for at kontrollere, om opgaven er afsluttet. Når vi er færdige, kan vi hente resultatet ved hjælp af metode.

2.3. Fuldført

Java 8 introduceret Fuldført med en kombination af en Fremtid og FuldførelseStage. Det giver forskellige metoder som supplyAsync, runAsyncog derefterApplyAsync til asynkron programmering.

Så lad os bruge Fuldført i stedet for FutureTask at finde et nummer på et nummer:

CompletableFuture completeableFuture = CompletableFuture.supplyAsync (() -> factorial (nummer)); while (! completeableFuture.isDone ()) {System.out.println ("CompletableFuture er ikke færdig endnu ..."); } langt resultat = completableFuture.get ();

Vi behøver ikke at bruge ExecutorService eksplicit. Det Fuldført internt brug ForkJoinPool at håndtere opgaven asynkront. Derfor gør det vores kode meget renere.

3. Guava

Guava giver den ListenableFuture klasse til at udføre asynkrone operationer.

Først tilføjer vi det seneste guava Maven afhængighed:

 com.google.guava guava 28.2-jre 

Lad os så finde et nummer på et nummer ved hjælp af ListenableFuture:

ExecutorService threadpool = Executors.newCachedThreadPool (); ListeningExecutorService service = MoreExecutors.listeningDecorator (threadpool); ListenableFuture guavaFuture = (ListenableFuture) service.submit (() -> factorial (nummer)); langt resultat = guavaFuture.get ();

Her, den Mere eksekutører klasse giver forekomsten af ListeningExecutorService klasse. Derefter ListeningExecutorService.submit metode udfører opgaven asynkront og returnerer forekomsten af ListenableFuture.

Guava har også en Fremtid klasse, der giver metoder som submitAsync, tidsplanAsyncog transformAsync at kæde ListenableFutures svarende til Fuldført

Lad os for eksempel se, hvordan du bruger Futures.submitAsync i stedet for ListeningExecutorService.submit metode:

ListeningExecutorService service = MoreExecutors.listeningDecorator (threadpool); AsyncCallable asyncCallable = Callables.asAsyncCallable (ny Callable () {public Long call () {return factorial (number);}}, service); ListenableFuture guavaFuture = Futures.submitAsync (asyncCallable, service);

Her, den submitAsync metoden kræver et argument af AsyncCallable, som oprettes ved hjælp af Callables klasse.

Derudover er Fremtid klasse giver addCallback metode til at registrere succes og fiasko tilbagekald:

Futures.addCallback (factorialFuture, ny FutureCallback () {public void onSuccess (Long factorial) {System.out.println (factorial);} public void onFailure (Throwable throw) {throw.getCause ();}}, service);

4. EA Async

Electronic Arts bragte den async-afventede funktion fra .NET til Java-økosystemet gennem ea-async bibliotek.

Biblioteket tillader skrivning af asynkron (ikke-blokerende) kode sekventielt. Derfor gør det asynkron programmering lettere og skaleres naturligt.

Først tilføjer vi det seneste ea-async Maven afhængighed af pom.xml:

 com.ea.async ea-async 1.2.3 

Lad os derefter transformere det tidligere diskuterede Fuldført kode ved hjælp af vente metode leveret af EA'er Async klasse:

statisk {Async.init (); } public long factorialUsingEAAsync (int number) {CompletableFuture completeableFuture = CompletableFuture.supplyAsync (() -> factorial (number)); langt resultat = Async.await (completableFuture); }

Her ringer vi til Async.init metode i statisk blokere for at initialisere Async runtime instrumentering.

Async instrumentering omdanner koden ved kørsel og omskriver opkaldet til vente metode til at opføre sig på samme måde som at bruge kæden af Fuldført.

Derfor, opkaldet til vente metode svarer til opkald Future. Slutte sig til.

Vi kan bruge - javaagent JVM-parameter til kompileringsinstrumentering. Dette er et alternativ til Async.init metode:

java -javaagent: ea-async-1.2.3.jar -cp 

Lad os undersøge et andet eksempel på at skrive asynkron kode sekventielt.

Først udfører vi et par kædeoperationer asynkront ved hjælp af kompositionsmetoderne som derefterComposeAsync og derefterAcceptAsync af Fuldført klasse:

CompletableFuture completableFuture = hej () .thenComposeAsync (hello -> mergeWorld (hej)) .thenAcceptAsync (helloWorld -> print (helloWorld)). Undtagelsesvis (kastbar -> {System.out.println (throwable.getCause ()); returner null ;}); completableFuture.get ();

Derefter kan vi omdanne koden ved hjælp af EA'er Async.await ():

prøv {String hej = afvente (hej ()); String helloWorld = afvente (mergeWorld (hej)); afventer (CompletableFuture.runAsync (() -> print (helloWorld))); } fange (Undtagelse e) {e.printStackTrace (); }

Implementeringen ligner den sekventielle blokeringskode. Men den vente metode blokerer ikke koden.

Som diskuteret, alle opkald til vente metoden vil blive omskrevet af Async instrumentering til at arbejde på samme måde som Future. Slutte sig til metode.

Så når den asynkrone udførelse af Hej metoden er færdig, Fremtid resultatet sendes til fusionere verden metode. Derefter sendes resultatet til den sidste udførelse ved hjælp af CompletableFuture.runAsync metode.

5. Kaktoer

Cactoos er et Java-bibliotek baseret på objektorienterede principper.

Det er et alternativ til Google Guava og Apache Commons, der giver almindelige objekter til udførelse af forskellige operationer.

Lad os først tilføje det seneste kaktoer Maven afhængighed:

 org.cactoos kaktoer 0.43 

Biblioteket tilbyder en Async klasse til asynkrone operationer.

Så vi kan finde et nummer på et nummer ved hjælp af Cactoos's forekomst Async klasse:

Async asyncFunction = ny Async (input -> faktor (input)); Fremtidig asyncFuture = asyncFunction.apply (nummer); langt resultat = asyncFuture.get ();

Her, det ansøge metode udfører operationen ved hjælp af ExecutorService.submit metode og returnerer en forekomst af Fremtid interface.

Tilsvarende er Async klasse har udføre metode, der giver den samme funktion uden en returværdi.

Bemærk: Cactoos-biblioteket er i de indledende udviklingsfaser og er muligvis ikke egnet til produktionsbrug endnu.

6. Jcabi-aspekter

Jcabi-Aspects leverer @Async kommentar til asynkron programmering gennem AspectJ AOP-aspekter.

Lad os først tilføje det seneste jcabi-aspekter Maven afhængighed:

 com.jcabi jcabi-aspekter 0.22.6 

Det jcabi-aspekter bibliotek kræver understøttelse af AspectJ runtime. Så vi tilføjer aspectjrt Maven afhængighed:

 org.aspectj aspectjrt 1.9.5 

Derefter tilføjer vi jcabi-maven-plugin plugin, der fletter binærfilerne med AspectJ-aspekter. Plugin giver ajc mål, der gør alt arbejdet for os:

 com.jcabi jcabi-maven-plugin 0.14.1 ajc org.aspectj aspectjtools 1.9.1 org.aspectj aspectjweaver 1.9.1 

Så vi er alle klar til at bruge AOP-aspekterne til asynkron programmering:

@Async @Loggable public Future factorialUsingAspect (int number) {Future factorialFuture = CompletableFuture.completedFuture (factorial (number)); tilbagevenden factorialFuture; }

Når vi kompilerer koden, biblioteket vil indsprøjte AOP - råd i stedet for @Async kommentar gennem AspectJ vævning, til asynkron udførelse af factorialUsingAspect metode.

Så lad os kompilere klassen ved hjælp af kommandoen Maven:

mvn installere

Outputtet fra jcabi-maven-plugin kan se ud som:

 --- jcabi-maven-plugin: 0.14.1: ajc (standard) @ java-async --- [INFO] jcabi-aspekter 0.18 / 55a5c13 startede en ny daemon-tråd jcabi-loggable til at se på @Loggable-bemærkede metoder [INFO] Uvævede klasser vil blive kopieret til / tutorials / java-async / target / unwoven [INFO] jcabi-aspect 0.18 / 55a5c13 startet ny daemon thread jcabi-cacheable til automatisk rengøring af udløbne @Cacheable værdier [INFO] ajc resultat: 10 fil (er) ) behandlet, 0 pointcut (s) vævet, 0 fejl (er), 0 advarsel (er)

Vi kan kontrollere, om vores klasse er vævet korrekt ved at kontrollere logfiler i jcabi-ajc.log fil, genereret af Maven-pluginet:

Deltag punkt 'metode-udførelse (java.util.concurrent.Future com.baeldung.async.JavaAsync.factorialUsingJcabiAspect (int))' i Type 'com.baeldung.async.JavaAsync' (JavaAsync.java:158) rådgivet af omkring rådgivning fra 'com.jcabi.aspects.aj.MethodAsyncRunner' (jcabi-aspect-0.22.6.jar! MethodAsyncRunner.class (fra MethodAsyncRunner.java))

Derefter kører vi klassen som et simpelt Java-program, og output ser ud som:

17: 46: 58.245 [main] INFO com.jcabi.aspects.aj.NamedThreads - jcabi-aspekter 0.22.6 / 3f0a1f7 startede en ny dæmontråd jcabi-loggable til at se @Loggable kommenterede metoder 17: 46: 58.355 [main] INFO com.jcabi.aspects.aj.NamedThreads - jcabi-aspekter 0.22.6 / 3f0a1f7 startede en ny daemon-tråd jcabi-async til asynkron metodeudførelse 17: 46: 58.358 [jcabi-async] INFO com.baeldung.async.JavaAsync - #factorialUsingJc (20): '[e-mail-beskyttet] [Udfyldes normalt]' i 44,64 µs

Så vi kan se en ny dæmontråd jcabi-async oprettes af det bibliotek, der udførte opgaven asynkront.

Tilsvarende er logning aktiveret af @Loggable kommentar leveret af biblioteket.

7. Konklusion

I denne artikel har vi set et par måder til asynkron programmering i Java.

Til at begynde med udforskede vi Java's indbyggede funktioner som f.eks FutureTask og Fuldført til asynkron programmering. Derefter har vi set et par biblioteker som EA Async og Cactoos med out-of-the-box-løsninger.

Vi undersøgte også understøttelsen af ​​at udføre opgaver asynkront ved hjælp af Guava's ListenableFuture og Fremtid klasser. Til sidst undersøgte vi jcabi-AspectJ-biblioteket, der giver AOP-funktioner gennem dets @Async kommentar til asynkron metodeopkald.

Som normalt er alle kodeimplementeringer tilgængelige på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found