Try using it in your preferred language.

English

  • English
  • 汉语
  • Español
  • Bahasa Indonesia
  • Português
  • Русский
  • 日本語
  • 한국어
  • Deutsch
  • Français
  • Italiano
  • Türkçe
  • Tiếng Việt
  • ไทย
  • Polski
  • Nederlands
  • हिन्दी
  • Magyar
translation

Dit is een door AI vertaalde post.

제이온

[Spring] @Async gebruiken

Selecteer taal

  • Nederlands
  • English
  • 汉语
  • Español
  • Bahasa Indonesia
  • Português
  • Русский
  • 日本語
  • 한국어
  • Deutsch
  • Français
  • Italiano
  • Türkçe
  • Tiếng Việt
  • ไทย
  • Polski
  • हिन्दी
  • Magyar

Samengevat door durumis AI

  • Deze handleiding legt uit hoe je Java-asynchrone verwerking kunt implementeren met behulp van Spring @Async, inclusief de voordelen en aandachtspunten.
  • Met Spring @Async kun je eenvoudig asynchrone verwerking implementeren door de @EnableAsync-annotatie toe te voegen en de @Async-annotatie toe te passen op de methoden die je asynchroon wilt verwerken.
  • Efficiënt threadbeheer is mogelijk via thread-poolinstellingen, en je kunt asynchrone verwerkingsresultaten teruggeven met behulp van Future, ListenableFuture, CompletableFuture, afhankelijk van het retourtype.

Java asynchrone verwerking

Voordat we Spring @Async bekijken, zijn de concepten van synchronisatie, asynchrone en multithreading essentieel. Aangenomen dat u bekend bent met deze concepten, laten we eens kijken naar pure Java asynchrone verwerking met code. Als u bekend bent met Java threads, kunt u deze sectie overslaan.


public class MessageService {

    public void print(String message) {
        System.out.println(message);
    }
}

public class Main {

    public static void main(String[] args) {
        MessageService messageService = new MessageService();

        for (int i = 1; i <= 100; i++) {
            messageService.print(i + "");
        }
    }


Als we een functie maken die een bericht ontvangt en het eenvoudigweg afdrukt met een synchrone methode, kunnen we de code schrijven zoals hierboven. Als we deze omzetten naar een multithreaded asynchrone methode, kunnen we de broncode schrijven zoals hieronder.


public class MessageService {

    public void print(String message) {
        new Thread(() -> System.out.println(message))
                .start();
    }
}

public class Main {

    public static void main(String[] args) {
        MessageService messageService = new MessageService();

        for (int i = 1; i <= 100; i++) {
            messageService.print(i + "");
        }
    }


Maar deze methode is erg riskant omdat we threads niet kunnen beheren. Als er bijvoorbeeld tegelijkertijd 10.000 aanvragen worden gedaan, moeten we in korte tijd 10.000 threads maken. Het maken van threads kost niet weinig, daarom heeft het een negatieve invloed op de prestaties van het programma en kan het zelfs een OOM-fout veroorzaken. Om threads te beheren, moeten we een threadpool implementeren en Java biedt de ExecutorService-klasse.


public class MessageService {

    private final ExecutorService executorService = Executors.newFixedThreadPool(10);

    public void print(String message) {
        executorService.submit(() -> System.out.println(message));
    }
}

public class Main {

    public static void main(String[] args) {
        MessageService messageService = new MessageService();

        for (int i = 1; i <= 100; i++) {
            messageService.print(i + "");
        }
    }


We hebben het aantal threads beperkt tot 10 en we kunnen de asynchrone verwerking op de juiste manier doen zoals we willen voor multithreading. Maar deze methode vereist dat we de submit()-methode van ExecutorService toepassen op elke methode die we asynchroon willen verwerken, wat betekent dat we herhaaldelijk moeten wijzigen. Dat wil zeggen, als we een methode die oorspronkelijk synchroon was geschreven, asynchroon willen maken, moeten we de logica van de methode zelf wijzigen.


Spring @Async

Een eenvoudige methode

@EnableAsync
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

@Service
public class MessageService {

    @Async
    public void print(String message) {
        System.out.println(message);
    }
}

@RequiredArgsConstructor
@RestController
public class MessageController {

    private final MessageService messageService;

    @GetMapping("/messages")
    @ResponseStatus(code = HttpStatus.OK)
    public void printMessage() {
        for (int i = 1; i <= 100; i++) {
            messageService.print(i + "");
        }
    }


Plak de @EnableAsync-annotatie boven de Application-klasse en plak de @Async-annotatie boven de methode met de synchrone logica die we asynchroon willen verwerken. Dat is alles. Maar deze methode heeft het probleem dat threads niet worden beheerd. Dit komt omdat de standaardinstelling van @Async is om SimpleAsyncTaskExecutor te gebruiken, wat geen threadpool is, maar alleen threads maakt.


Een methode om een threadpool te gebruiken

Verwijder eerst @EnableAsync uit de Application-klasse. Als de Application-klasse @EnableAutoConfiguration of @SpringBootApplication heeft ingesteld, wordt de threadPoolTaskExecutor-bean-informatie in de SpringAsyncConfig-klasse (die we later gaan maken) tijdens runtime ingeladen omdat @Configuration is ingesteld.


@Configuration
@EnableAsync // moet boven de Async-configuratieklasse worden geplaatst, niet boven Application.
public class SpringAsyncConfig {

    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(3); // Aantal standaardthreads
        taskExecutor.setMaxPoolSize(30); // Maximaal aantal threads
        taskExecutor.setQueueCapacity(100); // Grootte van de wachtrij
        taskExecutor.setThreadNamePrefix("Executor-");
        return taskExecutor;
    }


We kunnen de core- en max-grootte instellen. In dit geval zou u verwachten dat het met de initiële core-grootte werkt en dat het aantal threads wordt verhoogd tot de max-grootte als er geen taken meer kunnen worden verwerkt, maar dat is niet zo.


Intern maakt het een LinkedBlockingQueue met een grootte van Integer.MAX_VALUE aan, dus als de core-threads geen taken meer kunnen verwerken, wachten ze in de wachtrij. Als de wachtrij vol is, worden er threads gemaakt tot de max-grootte om de taken te verwerken.


Als u het niet prettig vindt om de grootte van de wachtrij in te stellen op Integer.MAX_VALUE, kunt u queueCapacity instellen. Als u het zo instelt, worden de taken eerst door 3 threads verwerkt. Als de verwerkingsnelheid lager is, worden de taken in de wachtrij geplaatst met een grootte van 100. Als er meer aanvragen binnenkomen, worden er maximaal 30 threads gemaakt om de taken te verwerken.


Nadat de threadpool is ingesteld, moet u de naam van de bean toevoegen in de methode die is geannoteerd met @Async.


@Service
public class MessageService {

    @Async("threadPoolTaskExecutor")
    public void print(String message) {
        System.out.println(message);
    }


Als u verschillende soorten threadpools wilt instellen, maakt u meerdere bean-creatiemethoden zoals threadPoolTaskExecutor() en voegt u de gewenste threadpool-bean in bij het instellen van @Async.


Het type retourwaarde per retourwaarde

Als er geen retourwaarde is

Dit is het geval wanneer de methode die asynchroon moet worden verwerkt, geen verwerkingsresultaten hoeft te leveren. In dit geval kunt u de retourwaarde van de @Async-annotatie instellen op void.


Als er een retourwaarde is

U kunt Future-, ListenableFuture- en CompletableFuture-typen gebruiken als retourtype. U kunt de retourvorm van de asynchrone methode inpakken in new AsyncResult().


[Toekomst]

@Service
public class MessageService {

    @Async
    public Future print(String message) throws InterruptedException {
        System.out.println("Taak starten - " + message);
        Thread.sleep(3000);
        return new AsyncResult<>("jayon-" + message);
    }
}

@RequiredArgsConstructor
@RestController
public class MessageController {

    private final MessageService messageService;

    @GetMapping("/messages")
    @ResponseStatus(code = HttpStatus.OK)
    public void printMessage() throws ExecutionException, InterruptedException {
        for (int i = 1; i <= 5; i++) {
            Future future = messageService.print(i + "");
            System.out.println(future.get());
        }
    }
}

// Uitvoerresultaat
Taak starten - 1
jayon-1
Taak starten - 2
jayon-2
Taak starten - 3
jayon-3
Taak starten - 4
jayon-4
Taak starten - 5


future.get() blokkeert en wacht tot het resultaat van de aanvraag is ontvangen. Dit maakt het een asynchroon blokkerend mechanisme, wat betekent dat het niet goed presteert. Future wordt meestal niet gebruikt.


[ListenableFuture]

@Service
public class MessageService {

    @Async
    public ListenableFuture print(String message) throws InterruptedException {
        System.out.println("Taak starten - " + message);
        Thread.sleep(3000);
        return new AsyncResult<>("jayon-" + message);
    }
}

@RequiredArgsConstructor
@RestController
public class MessageController {

    private final MessageService messageService;

    @GetMapping("/messages")
    @ResponseStatus(code = HttpStatus.OK)
    public void printMessage() throws InterruptedException {
        for (int i = 1; i <= 5; i++) {
            ListenableFuture listenableFuture = messageService.print(i + "");
            listenableFuture.addCallback(System.out::println, error -> System.out.println(error.getMessage()));
        }
    }
}

// Uitvoerresultaat
Taak starten - 1
Taak starten - 3
Taak starten - 2
jayon-1
jayon-2
Taak starten - 5
jayon-3
Taak starten - 4
jayon-4


ListenableFuture maakt het mogelijk om taken asynchroon en zonder blokkades te verwerken via callbacks. De eerste parameter van de addCallback()-methode definieert de callback-methode voor taakvoltooiing en de tweede parameter definieert de callback-methode voor taakfouten. Ter informatie: aangezien de core-threads van de threadpool op 3 zijn ingesteld, kunt u zien dat de "Taak starten"-berichten aanvankelijk 3 keer worden weergegeven.



[CompletableFuture]

We kunnen niet-blokkerende logica implementeren met alleen ListenableFuture, maar als er een callback binnen een callback nodig is, wordt de code erg complex, wat wordt aangeduid als callback-hell.


Untitled


Natuurlijk zullen we CompletableFuture deze keer niet in detail bespreken, dus als u nieuwsgierig bent naar code die complexe callbacks aanpakt, raadpleeg dan de link naar de bron in de onderstaande bron.


@Service
public class MessageService {

    @Async
    public CompletableFuture print(String message) throws InterruptedException {
        System.out.println("Taak starten - " + message);
        Thread.sleep(3000);
        return new AsyncResult<>("jayon-" + message).completable();
    }
}

@RequiredArgsConstructor
@RestController
public class MessageController {

    private final MessageService messageService;

    @GetMapping("/messages")
    @ResponseStatus(code = HttpStatus.OK)
    public void printMessage() throws InterruptedException {
        for (int i = 1; i <= 5; i++) {
            CompletableFuture completableFuture = messageService.print(i + "");
            completableFuture
                    .thenAccept(System.out::println)
                    .exceptionally(error -> {
                        System.out.println(error.getMessage());
                        return null;
                    });
        }
    }


Het heeft een betere leesbaarheid dan de callback-definitie van ListenableFuture en voert niet-blokkerende functionaliteit perfect uit. Daarom wordt het gebruik van CompletableFuture aanbevolen wanneer u @Async gebruikt en er een retourwaarde nodig is.


Voordelen van @Async

Ontwikkelaars kunnen methoden synchroon schrijven en vervolgens eenvoudig de @Async-annotatie boven de methode plaatsen als ze asynchrone functionaliteit willen. Dit maakt het mogelijk om code te maken die gemakkelijk te onderhouden is voor zowel synchronisatie als asynchrone methoden.


Voorzorgsmaatregelen bij @Async

Om de @Async-functionaliteit te gebruiken, moet u de @EnableAsync-annotatie declareren. Als u geen aparte instelling instelt, werkt deze in de proxymodus. Met andere woorden, alle asynchrone methoden die werken met de @Async-annotatie volgen de beperkingen van Spring AOP. Zie voor meer informatiedeze post.


  • AOP werkt niet als u @Async aan een private methode plaatst.
  • AOP werkt niet als methoden in hetzelfde object elkaar aanroepen.


Bron

제이온
제이온
제이온
제이온
[Java] Gesynchroniseerde verzameling versus gelijktijdige verzameling Gesynchroniseerde verzamelingen in Java (Vector, Hashtable, Collections.synchronizedXXX) zorgen voor gelijktijdigheid in een multi-threaded omgeving, maar kunnen leiden tot prestatieverlies en problemen wanneer meerdere bewerkingen worden gecombineerd. Al

25 april 2024

[Spring] Wat zijn Filter, Interceptor en Argument Resolver? Leer meer over de concepten en verschillen van filters, interceptors en argument resolvers in Spring-webtoepassingen. We bespreken hoe elk van deze functies wordt geïmplementeerd, wanneer ze worden gebruikt, hun voor- en nadelen en een vergelijking van de

27 april 2024

[Effectieve Java] Item 6. Vermijd onnodige objectcreatie Een gids over het verminderen van onnodige objectcreatie in Java. Voor onveranderlijke objecten zoals String en Boolean is het beter om literals te gebruiken, en voor reguliere expressies is het beter om Pattern-instanties te cachen. Autoboxing kan ook le

28 april 2024

[Concurrency] Atomaire bewerking: Memory Fence en Memory Ordering Deze blogpost bespreekt hoe geheugensorde te overwegen in atomaire bewerkingen en het belang van de Ordering-opties. We bespreken verschillende Ordering-opties zoals Relaxed, Acquire, Release, AcqRel, SecCst, inclusief een beschrijving van de voor- en nad
곽경직
곽경직
곽경직
곽경직
곽경직

12 april 2024

Hoe Rust concurrency-fouten voorkomt Rust is een krachtige taal die de uitdagingen van concurrency-programmeren aanpakt. Door het type-systeem en eigendomsmodel is het veilig om gegevens tussen threads te verzenden en te delen. Met behulp van interne veranderlijkheidspatronen zoals Mutex, Ch
곽경직
곽경직
곽경직
곽경직
곽경직

28 maart 2024

[Niet-major, overleven als ontwikkelaar] 14. Samenvatting van veelgestelde technische interviewvragen voor beginnende ontwikkelaars Deze gids is bedoeld om beginnende ontwikkelaars te helpen met de voorbereiding op technische interviews. Het behandelt concepten die vaak ter sprake komen tijdens interviews, zoals het hoofdgeheugengebied, gegevensstructuren, RDBMS en NoSQL, procedurele
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

3 april 2024

Uitdagingen bij het ontwikkelen van de Korea Investment & Securities API Deze blogpost behandelt de uitdagingen en oplossingen die zijn opgedoken tijdens het ontwikkelen van de Korea Investment & Securities API. De post bespreekt ervaringen en tips van een ontwikkelaar over onderwerpen zoals het openen van een rekening, het ov
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마

23 april 2024

De basisstructuur van geautomatiseerde aandelenhandel (wordt bijgewerkt...) De ontwikkeling van een geautomatiseerd aandelenhandelprogramma wordt stapsgewijs in detail beschreven, van het openen van een rekening tot het voorbereiden van de ontwikkelomgeving, API-integratie, UI-ontwerp, implementatie van kernlogica, tot en met de
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마

22 april 2024

한국투자증권 API-ontwikkelingsreferentie Deze blogpost beschrijft het proces van het verzamelen van de benodigde informatie voor het ontwikkelen van een geautomatiseerd handelsprogramma met behulp van de 한국투자증권 API. U kunt verschillende bronnen gebruiken, zoals de officiële website, het KIS Deve
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마

22 april 2024