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

Ez egy AI által fordított bejegyzés.

제이온

[Spring] @Async használatának módja

  • Írás nyelve: Koreai
  • Referencia ország: Minden ország country-flag

Válasszon nyelvet

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

A durumis AI által összefoglalt szöveg

  • Bemutatja a Java aszinkron feldolgozás Spring @Async használatával történő implementálásának módját, előnyeit és figyelmeztetéseit.
  • A Spring @Async használatával az @EnableAsync annotáció hozzáadásával és az aszinkron feldolgozásra szánt metódusokhoz az @Async annotáció hozzárendelésével egyszerűen implementálhatja az aszinkron feldolgozást.
  • A szálkészlet beállításával hatékony szálakezelés érhető el, és a visszatérési típus függvényében használhatja a Future, ListenableFuture, CompletableFuture stb. a aszinkron feldolgozás eredményeinek visszaadására.

Java aszinkron feldolgozás

Mielőtt megvizsgálnánk a Spring @Async-et, elengedhetetlen az szinkron, aszinkron és a többszálas fogalmak ismerete. Feltételezve, hogy ismerjük ezeket a fogalmakat, nézzük meg a tiszta Java aszinkron feldolgozás módját kód formájában. Ha járatos vagy a Java szálakban, akkor ezt a fejezetet kihagyhatod.


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 + "");
        }
    }


Ha egy szinkron módon létrehozunk egy üzenet fogadására és egyszerűen kiírásra szolgáló funkciót, akkor a kódot a fent látható módon írhatjuk meg. Ha ezt többszálas aszinkron módon akarjuk megváltoztatni, akkor a következőképpen írhatjuk meg a forráskódot.


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 + "");
        }
    }


Ez a módszer azonban nagyon veszélyes, mert nem tudjuk kezelni a szálakat. Például, ha egyszerre 10 000 kérés érkezik, akkor nagyon rövid idő alatt 10 000 szálnak kellene létrejönnie. A szál létrehozásának költsége nem elhanyagolható, ezért befolyásolhatja a program teljesítményét, sőt OOM hiba is előfordulhat. Ezért szükséges a szálak kezelésére szálkészlet (Thread Pool) implementálása, amelyet Java az ExecutorService osztályban biztosít.


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 + "");
        }
    }


A szálak teljes számát 10-re korlátoztuk, és most már helyesen tudjuk végrehajtani a többszálas aszinkron feldolgozást. Ez a módszer azonban minden aszinkron módon feldolgozni kívánt metódusnál megköveteli az ExecutorService submit() metódusának alkalmazását, így ismétlődő módosításokat kell végeznünk. Más szavakkal, ha az elején szinkron logikával írtunk egy metódust, és azt aszinkronná akarjuk változtatni, akkor elkerülhetetlen a metódus maga logikájának módosítása.


Spring @Async

Egyszerű módszer

@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 + "");
        }
    }


Csupán az @EnableAsync annotációt kell hozzáadnunk az Application osztályhoz, és az @Async annotációt az aszinkron módon feldolgozni kívánt szinkron logikájú metódusokhoz. Ez a módszer azonban nem kezeli a szálakat. Miért? Mert az @Async alapértelmezett beállítása a SimpleAsyncTaskExecutor használatát határozza meg, amely nem szálkészlet, hanem egyszerűen szálakat hoz létre.


Szálkészlet használata

Először is távolítsuk el az @EnableAsync-et az Application osztályból. Ha az Application osztályban @EnableAutoConfiguration vagy @SpringBootApplication beállítás található, akkor futásidőben beolvassa a SpringAsyncConfig osztály (amelyet alább fogunk létrehozni) threadPoolTaskExecutor bean információit, amelyen @Configuration beállítás található.


@Configuration
@EnableAsync // Az Application helyett az aszinkron beállításos osztályhoz kell hozzáadni.
public class SpringAsyncConfig {

    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(3); // Alapértelmezett szálak száma
        taskExecutor.setMaxPoolSize(30); // Maximális szálak száma
        taskExecutor.setQueueCapacity(100); // A sor mérete
        taskExecutor.setThreadNamePrefix("Executor-");
        return taskExecutor;
    }


Beállíthatjuk a core és a max méretét. Elvárhatnánk, hogy a kezdeti core mérettel működjön, és ha nem tudja feldolgozni a további feladatokat, akkor a max méretig növekedjen a szálak száma. De ez nem így van.


Belsőleg egy Integer.MAX_VALUE méretű LinkedBlockingQueue jön létre, amelybe a core méretű szálak feldolgozzák a feladatokat. Ha a szálak nem tudják feldolgozni a feladatokat, akkor a sorban várnak. Ha a sor megtelt, akkor a maximális méretű szálak jönnek létre, amelyek feldolgozzák a feladatokat.


Ha a sor mérete túl nagy az Integer.MAX_VALUE-hoz képest, akkor beállíthatjuk a queueCapacity-t. A fenti beállítások alapján a kezdeti 3 szál feldolgozza a feladatokat. Ha a feldolgozási sebesség lelassul, akkor a feladatokat a 100 méretű sorban tárolják. Ha további kérelmek érkeznek, akkor legfeljebb 30 szál jön létre a feladatok feldolgozására.


Miután beállítottuk a szálkészletet, az @Async annotációval ellátott metódusokhoz csak a bean nevét kell hozzáadnunk.


@Service
public class MessageService {

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


Ha több szálkészlet típusát szeretnénk beállítani, akkor több bean létrehozó metódust kell létrehoznunk (például threadPoolTaskExecutor()), és az @Async beállításakor az kívánt szálkészlet bean-t kell megadnunk.


A visszatérési típus szerinti visszatérési forma

Visszatérési érték nélküli eset

Ez a helyzet akkor áll fenn, ha az aszinkron feldolgozásra szánt metódusnak nincs szüksége arra, hogy visszaküldjön egy eredményt. Ebben az esetben az @Async annotáció visszatérési típusát void-ra kell beállítani.


Visszatérési értékű eset

A Future, ListenableFuture és CompletableFuture típusokat használhatjuk visszatérési típusként. Az aszinkron metódus visszatérési formáját a new AsyncResult() segítségével kell becsomagolni.


[Future]

@Service
public class MessageService {

    @Async
    public Future print(String message) throws InterruptedException {
        System.out.println("Task Start - " + 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());
        }
    }
}

// Végrehajtási eredmény
Task Start - 1
jayon-1
Task Start - 2
jayon-2
Task Start - 3
jayon-3
Task Start - 4
jayon-4
Task Start - 5


A future.get() blokkoló módon vár a kérés eredményére, amíg az meg nem érkezik. Ezért blokkoló aszinkron módszerré válik, ami nem túl hatékony. A Future-t általában nem használják.


[ListenableFuture]

@Service
public class MessageService {

    @Async
    public ListenableFuture print(String message) throws InterruptedException {
        System.out.println("Task Start - " + 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()));
        }
    }
}

// Végrehajtási eredmény
Task Start - 1
Task Start - 3
Task Start - 2
jayon-1
jayon-2
Task Start - 5
jayon-3
Task Start - 4
jayon-4


A ListenableFuture segítségével hívásfüggvényekkel (callback) nem blokkoló módon végezhetünk feldolgozást. Az addCallback() metódus első paramétere a feladat befejeződésének hívásfüggvénye, a második paraméter pedig a feladat hibájának hívásfüggvénye. Megjegyzés: A szálkészlet core méretét 3-ra állítottuk be, ezért a "Task Start" üzenet kezdetben 3-szor jelenik meg.



[CompletableFuture]

A ListenableFuture segítségével is megvalósíthatunk nem blokkoló logikát, de ha a hívásfüggvényen belül egy újabb hívásfüggvényre van szükségünk, akkor a kód nagyon összetett lesz, és a "hívásfüggvény-pokol" nevű hibát okozhatja.


Untitled


Természetesen ebben a tanfolyamban nem foglalkozunk részletesen a CompletableFuture-rel, így ha érdekelnek az összetett hívásfüggvények kezelésére szolgáló kódok, akkor kérjük, olvass el a következő linkeket.


@Service
public class MessageService {

    @Async
    public CompletableFuture print(String message) throws InterruptedException {
        System.out.println("Task Start - " + 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;
                    });
        }
    }


A ListenableFuture hívásfüggvények definiálásához képest jobb az olvashatósága, és teljesen nem blokkoló módon hajtja végre a feladatokat. Ezért az @Async használatakor, ha visszatérési értékre van szükségünk, akkor a CompletableFuture használatát ajánljuk.


Az @Async előnyei

A fejlesztők szinkron módon írhatják a metódusokat, és ha aszinkronra van szükségük, akkor egyszerűen hozzáadhatják az @Async annotációt a metódushoz. Így a szinkron és aszinkron kódok könnyebben karbantarthatók.


Az @Async figyelmeztetései

Az @Async funkció használatához az @EnableAsync annotációt kell deklarálnunk. Ha nem adunk meg külön beállításokat, akkor az a proxy üzemmódban működik. Ez azt jelenti, hogy az @Async annotációval működő aszinkron metódusok mind betartják a Spring AOP korlátozásait. További információért lásd ezt a bejegyzést.


  • Ha az @Async annotációt privát metódusokhoz adjuk hozzá, akkor az AOP nem fog működni.
  • Ha ugyanazon az objektumon belül lévő metódusokat hívunk meg egymásból, akkor az AOP nem fog működni.


Források

제이온
제이온
제이온
제이온
[Java] Szinkronizált gyűjtemény vs. egyidejű gyűjtemény A Java szinkronizált gyűjteményei (Vector, Hashtable, Collections.synchronizedXXX) garantálják az egyidejűséget többszálas környezetben, de teljesítménycsökkenést okozhatnak, és problémákat okozhatnak, ha több műveletet egybegyűjtve használnak. Alternatív

2024. április 25.

[Spring] Mi a Filter, Interceptor és Argument Resolver? Fedezze fel a Spring webes alkalmazásokban a kérések feldolgozására használt szűrők, interceperek és Argument Resolver fogalmát és különbségeit. Az egyes funkciók megvalósítási módjainak, használati időpontjainak és előnyeinek/hátrányainak összehasonlító

2024. április 27.

[Hatékony Java] 6. pont: Kerülje a felesleges objektum létrehozását Útmutató a Java-ban a felesleges objektum létrehozásának minimalizálásához. A String, Boolean és egyéb immutabilis objektumok esetében célszerű literálokat használni, míg a reguláris kifejezéseknél a Pattern példányokat érdemes gyorsítótárazni. Emellett a

2024. április 28.

[Szinkronitás] Atomi művelet: Memória kerítés és memória sorrendezés Ez a bejegyzés bemutatja, hogyan kell figyelembe venni a memória sorrendet atomi műveletekben, és megmagyarázza a sorrendezési beállítások fontosságát. Bemutatja a Relaxed, Acquire, Release, AcqRel, SecCst és egyéb sorrendezési beállításokat, valamint rés
곽경직
곽경직
곽경직
곽경직
곽경직

2024. április 12.

Hogyan akadályozza meg a Rust a párhuzamosítási hibákat A Rust egy erőteljes nyelv, amely megoldja a párhuzamos programozás kihívásait. A típusrendszere és a tulajdonmodell biztosítja az adatok biztonságos átvitelét és megosztását a szálak között. A belső változtathatóság mintái, mint a Mutex, a Channel és az
곽경직
곽경직
곽경직
곽경직
곽경직

2024. március 28.

[Nem informatikai szakember, de fejlesztőként akarok túlélni] 14. Gyakran feltett technikai interjúkérdések összefoglalása kezdő fejlesztők számára Útmutató a kezdő fejlesztők számára a technikai interjúra való felkészüléshez. A fő memóriaterület, adatstruktúrák, RDBMS és NoSQL, eljárási és objektumorientált, átírás és túlterhelés, oldalcserélő algoritmusok, folyamatok és szálak, OSI 7-réteg, TCP és
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

2024. április 3.

Nyílt forráskódú AI teljes verem Az AI ökoszisztémában egyre több nyílt forráskódú LLM (nagy nyelvi modell) jelenik meg. Megjelentek a Mistral, a Llama és a phi-2, amelyek erőteljes teljesítményt és nyílt licencet kínálnak, és a használatukhoz számos eszköz is fejlesztés alatt áll. A Lan
RevFactory
RevFactory
RevFactory
RevFactory

2024. február 5.

Hogyan használjuk a Slack csatornák szekció opcióit? A Slack fizetős verziójában (Pro) megtudhatja, hogyan rendezi a csatornákat szekciókba. Vigye a kurzort a csatorna nevére, kattintson a három pontra, és válassza a "Szekció létrehozása" lehetőséget. A csatornák a húzással és dobással hozzáadhatók a szekci
여행가고싶은블로거지만여행에대해다루진않을수있어요
여행가고싶은블로거지만여행에대해다루진않을수있어요
여행가고싶은블로거지만여행에대해다루진않을수있어요
여행가고싶은블로거지만여행에대해다루진않을수있어요
여행가고싶은블로거지만여행에대해다루진않을수있어요

2024. május 2.

Csapatmunka eszköz, Slack: csatorna törlése Tudja meg, hogyan törölhet egy csatornát a Slackben. A csatorna törlésekor választhatja, hogy megőrzi-e a csatorna üzeneteit, vagy törli-e azokat. A ingyenes verzióban csak a legutóbbi 90 napban küldött 10 000 üzenetet lehet keresni, ezért célszerű megőri
여행가고싶은블로거지만여행에대해다루진않을수있어요
여행가고싶은블로거지만여행에대해다루진않을수있어요
Útmutató a csatorna törléséhez a Slackben
여행가고싶은블로거지만여행에대해다루진않을수있어요
여행가고싶은블로거지만여행에대해다루진않을수있어요

2024. március 6.