제이온

[Spring] Jak korzystać z @Async

  • Język oryginalny: Koreański
  • Kraj: Wszystkie krajecountry-flag
  • TO

Utworzono: 2024-04-25

Utworzono: 2024-04-25 22:33

Przetwarzanie asynchroniczne w Javie

Zanim przyjrzymy się Spring @Async, konieczne jest zrozumienie pojęć synchronizacji, asynchroniczności i wielowątkowości. Zakładamy, że znasz te pojęcia i przyjrzymy się sposobom przetwarzania asynchronicznego w czystej Javie za pomocą kodu. Jeśli dobrze znasz wątki Java, możesz pominąć ten rozdział.



Jeśli stworzymy funkcję, która po prostu odbiera wiadomość i ją wyświetla, w sposób synchroniczny, to kod będzie wyglądał jak powyżej. Zmieniając to na wielowątkową wersję asynchroniczną, możemy napisać kod źródłowy w następujący sposób.



Jednak ta metoda jest bardzo niebezpieczna, ponieważ nie pozwala na zarządzanie wątkami. Na przykład, jeśli jednocześnie zostanie wykonanych 10 000 wywołań, w bardzo krótkim czasie trzeba będzie utworzyć 10 000 wątków. Koszt tworzenia wątków nie jest mały, więc może negatywnie wpłynąć na wydajność programu, a nawet spowodować błąd OOM. Dlatego, aby zarządzać wątkami, należy zaimplementować pulę wątków, a Java oferuje klasę ExecutorService.



Ograniczamy całkowitą liczbę wątków do 10 i możemy poprawnie wykonać żądaną asynchroniczną obróbkę w trybie wielowątkowym. Jednak w powyższej metodzie dla każdej metody, którą chcemy obsłużyć asynchronicznie, musimy zastosować metodę submit() klasy ExecutorService, co wymaga powtarzalnych modyfikacji. Oznacza to, że jeśli na początku chcemy zmienić metodę zapisaną w logice synchronicznej na asynchroniczną, nie można uniknąć zmiany samej logiki metody.


Spring @Async

Prosty sposób


Wystarczy dołączyć adnotację @EnableAsync nad klasą Application i adnotację @Async nad metodą synchroniczną, którą chcemy obsłużyć asynchronicznie. Jednak ten sposób ma problem z brakiem zarządzania wątkami. Dzieje się tak, ponieważ domyślne ustawienie @Async używa SimpleAsyncTaskExecutor, który nie jest pulą wątków, a jedynie tworzy wątki.


Sposób użycia puli wątków

Najpierw usuwamy @EnableAsync z klasy Application. Jeśli w klasie Application jest ustawienie @EnableAutoConfiguration lub @SpringBootApplication, w czasie wykonywania odczytuje informacje o fasoli threadPoolTaskExecutor z klasy SpringAsyncConfig (utworzonej poniżej), która jest skonfigurowana jako @Configuration.



Możemy ustawić rozmiary core i max. W tym momencie możemy oczekiwać, że początkowo będzie działać tyle wątków, ile wynosi rozmiar core, a jeśli nie będzie można obsłużyć większej liczby zadań, liczba wątków wzrośnie do rozmiaru max. Jednak tak się nie dzieje.


Wewnątrz tworzona jest kolejka LinkedBlockingQueue o rozmiarze Integer.MAX_VALUE, więc jeśli core wątków nie może obsłużyć zadania, czeka ono w kolejce. Dopiero gdy kolejka jest pełna, tworzonych jest max wątków w celu obsługi zadań.


Jeśli ustawienie rozmiaru kolejki na Integer.MAX_VALUE jest zbyt uciążliwe, można ustawić queueCapacity. Jeśli ustawimy to tak, jak powyżej, to początkowo 3 wątki będą obsługiwać zadania, a jeśli szybkość obsługi zadań spadnie, zadania będą umieszczane w kolejce o rozmiarze 100, a jeśli nadejdą dalsze żądania, zostanie utworzonych maksymalnie 30 wątków w celu obsługi zadań.


Po skonfigurowaniu puli wątków wystarczy podać nazwę tej fasoli w metodzie z adnotacją @Async.



Jeśli chcesz ustawić kilka rodzajów puli wątków, możesz utworzyć kilka metod tworzenia fasoli, takich jak threadPoolTaskExecutor(), a podczas ustawiania @Async możesz wstawić żądaną fasolę puli wątków.


Forma zwracana w zależności od typu zwracanego

Brak wartości zwracanej

Jest to przypadek, gdy metoda, którą należy przetworzyć asynchronicznie, nie musi przekazywać wyniku przetwarzania. W takim przypadku typem zwracanym adnotacji @Async jest void.


Istnieje wartość zwracana

Można użyć typów Future, ListenableFuture i CompletableFuture jako typów zwracanych. Formę zwracaną metody asynchronicznej można zamknąć w new AsyncResult().


[Future]


future.get() blokuje i czeka na wynik żądania. Dlatego staje się to synchroniczną blokującą metodą, co nie jest dobre dla wydajności. Zazwyczaj Future nie jest używane.


[ListenableFuture]


ListenableFuture umożliwia przetwarzanie zadań w sposób nieblokujący za pomocą mechanizmu zwrotnego. W metodzie addCallback() pierwszym parametrem jest metoda zwrotnego wywołania po zakończeniu zadania, a drugim parametrem jest metoda zwrotnego wywołania w przypadku błędu zadania. Należy zauważyć, że ponieważ liczba rdzeni puli wątków jest ustawiona na 3, można zauważyć, że wiadomość „Task Start” jest wyświetlana 3 razy na początku.



[CompletableFuture]

Chociaż ListenableFuture pozwala na zaimplementowanie logiki nieblokującej, jeśli konieczne jest zagnieżdżenie zwrotnego wywołania w zwrotnym wywołaniu, może to doprowadzić do bardzo skomplikowanego kodu, zwanego „piekłem zwrotnego wywołania”.


<span class="image-inline ck-widget" contenteditable="false"><img src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F9f152db8-c015-43cd-bf13-85c594f5f218%2FUntitled.png?table=block&id=268ac0bc-ca7b-4bcb-b11b-ac611a5038b2&spaceId=b453bd85-cb15-44b5-bf2e-580aeda8074e&width=2000&userId=80352c12-65a4-4562-9a36-2179ed0dfffb&cache=v2" alt="Untitled" style="aspect-ratio:2000/1455;" width="2000" height="1455"></span>


Oczywiście w tym przypadku nie będziemy szczegółowo omawiać CompletableFuture, więc jeśli jesteś zainteresowany kodem radzenia sobie ze złożonymi zwrotnymi wywołaniami, zapoznaj się z linkiem w poniższym źródle.



Definicja zwrotnego wywołania w ListenableFuture jest łatwiejsza do odczytania, a funkcja nieblokująca działa w pełni. Dlatego zaleca się używanie CompletableFuture, gdy używasz @Async i potrzebujesz wartości zwracanej.


Zalety @Async

Deweloperzy mogą pisać metody w sposób synchroniczny, a jeśli chcą, aby działały one asynchronicznie, wystarczy dodać adnotację @Async nad metodą. Dzięki temu można tworzyć kod, który jest łatwy w utrzymaniu zarówno w przypadku synchronizacji, jak i asynchroniczności.


Uwagi dotyczące @Async

Aby użyć funkcji @Async, należy zadeklarować adnotację @EnableAsync, ale jeśli nie zostanie dokonana żadna dodatkowa konfiguracja, będzie ona działać w trybie proxy. Oznacza to, że wszystkie metody asynchroniczne działające z adnotacją @Async będą podlegać ograniczeniom Spring AOP. Aby uzyskać więcej informacji, zapoznaj się z tym wpisem.


  • Jeśli adnotacja @Async zostanie dołączona do metody prywatnej, AOP nie zadziała.
  • Jeśli metody w tym samym obiekcie wywołują się wzajemnie, AOP nie zadziała.


Źródła

Komentarze0

[Dla osób bez informatycznego wykształcenia, jak przetrwać jako programista] 14. Podsumowanie często zadawanych pytań na rozmowach kwalifikacyjnych dla początkujących programistówPodsumowując, przedstawiamy często zadawane pytania techniczne na rozmowach kwalifikacyjnych dla programistów (obszar pamięci, struktury danych, bazy danych itd.). Mamy nadzieję, że pomoże to w przygotowaniach do rozmowy kwalifikacyjnej.
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

April 3, 2024