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

Ini adalah postingan yang diterjemahkan oleh AI.

제이온

[Spring] Cara Menggunakan @Async

  • Bahasa penulisan: Bahasa Korea
  • Negara referensi: Semua negara country-flag

Pilih Bahasa

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

Teks yang dirangkum oleh AI durumis

  • Artikel ini menjelaskan cara mengimplementasikan pemrosesan asinkron Java menggunakan Spring @Async, keuntungannya, dan hal-hal yang perlu diperhatikan.
  • Dengan menggunakan Spring @Async, Anda dapat dengan mudah mengimplementasikan pemrosesan asinkron dengan menambahkan anotasi @EnableAsync dan menempelkan anotasi @Async pada metode yang ingin Anda proses secara asinkron.
  • Konfigurasi thread pool memungkinkan manajemen thread yang efisien, dan berdasarkan tipe kembaliannya, Anda dapat menggunakan Future, ListenableFuture, atau CompletableFuture untuk mengembalikan hasil pemrosesan asinkron.

Pemrosesan Asinkron Java

Sebelum melihat Spring @Async, penting untuk memahami konsep sinkron, asinkron, dan multithreading. Dengan asumsi Anda memahami konsep ini, mari kita lihat cara pemrosesan asinkron Java murni melalui kode. Jika Anda sudah familier dengan Java threading, Anda dapat melewati bab ini.


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


Jika Anda membuat fungsi untuk menerima message dan hanya menampilkannya secara sinkron, Anda dapat menulis kode seperti di atas. Jika Anda mengubahnya menjadi cara asinkron multi-threading, Anda dapat menulis kode sumber seperti ini.


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


Namun, metode ini sangat berbahaya karena Anda tidak dapat mengelola Thread. Misalnya, jika ada 10.000 panggilan secara bersamaan, Anda harus membuat 10.000 Thread dalam waktu yang sangat singkat. Biaya membuat Thread tidak sedikit, sehingga dapat berdampak buruk pada kinerja program, dan bahkan dapat menyebabkan kesalahan OOM. Oleh karena itu, Anda harus mengimplementasikan Thread Pool untuk mengelola Thread, dan Java menyediakan kelas ExecutorService.


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


Kami membatasi jumlah total Thread menjadi 10, dan kami dapat melakukan pemrosesan asinkron multi-threading yang kami inginkan dengan benar. Namun, metode di atas mengharuskan Anda untuk menerapkan metode submit() dari ExecutorService ke setiap metode yang ingin Anda proses secara asinkron, sehingga Anda harus melakukan pekerjaan perbaikan berulang. Artinya, jika Anda ingin mengubah metode yang awalnya ditulis sebagai logika sinkron menjadi asinkron, perubahan pada logika metode itu sendiri tidak dapat dihindari.


Spring @Async

Cara Sederhana

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


Tempelkan anotasi @EnableAsync di atas kelas Aplikasi Anda, dan tempelkan anotasi @Async di atas metode logika sinkron yang ingin Anda proses secara asinkron. Itu saja. Namun, metode di atas memiliki masalah dalam tidak mengelola thread. Ini karena pengaturan default @Async adalah untuk menggunakan SimpleAsyncTaskExecutor, yang bukan kumpulan thread dan hanya berfungsi untuk membuat thread.


Cara Menggunakan Thread Pool

Pertama, hapus @EnableAsync dari kelas Aplikasi Anda. Jika kelas Aplikasi Anda memiliki pengaturan @EnableAutoConfiguration atau @SpringBootApplication, maka pada saat runtime, ia akan membaca informasi tentang bean threadPoolTaskExecutor (yang akan kita buat di bawah) dari kelas SpringAsyncConfig yang dikonfigurasi @Configuration.


@Configuration
@EnableAsync // Harus dilampirkan pada kelas konfigurasi Async, bukan pada Aplikasi
public class SpringAsyncConfig {

    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(3); // Jumlah thread default
        taskExecutor.setMaxPoolSize(30); // Jumlah thread maksimum
        taskExecutor.setQueueCapacity(100); // Ukuran Queue
        taskExecutor.setThreadNamePrefix("Executor-");
        return taskExecutor;
    }


Anda dapat mengatur ukuran core dan max. Pada saat ini, Anda mungkin berharap bahwa ia akan beroperasi dengan ukuran core awal dan kemudian meningkatkan jumlah Thread hingga ukuran max jika tidak dapat memproses tugas lagi. Namun, itu tidak benar.


Secara internal, ia membuat LinkedBlockingQueue dengan ukuran Integer.MAX_VALUE dan menunggu di Queue jika Thread dengan ukuran core tidak dapat memproses tugas. Jika Queue penuh, maka ia akan membuat Thread dengan ukuran max untuk memproses tugas.


Jika Anda merasa tidak nyaman menetapkan ukuran Queue menjadi Integer.MAX_VALUE, Anda dapat mengatur queueCapacity. Dengan pengaturan seperti di atas, ia akan diproses oleh 3 Thread awal, dan jika kecepatan pemrosesan melambat, tugas akan ditempatkan di Queue berukuran 100. Jika ada permintaan lebih lanjut, ia akan membuat hingga 30 Thread untuk memproses tugas.


Setelah konfigurasi kumpulan thread selesai, Anda dapat melampirkan nama bean di atas ke metode yang memiliki anotasi @Async.


@Service
public class MessageService {

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


Jika Anda ingin menetapkan beberapa jenis kumpulan thread, buat beberapa metode pembuatan bean seperti threadPoolTaskExecutor(), dan masukkan bean kumpulan thread yang Anda inginkan saat mengatur @Async.


Bentuk Pengembalian Berdasarkan Jenis Pengembalian

Jika tidak ada nilai pengembalian

Ini adalah kasus ketika metode yang harus diproses secara asinkron tidak perlu mengirimkan hasil pemrosesan. Dalam kasus ini, atur tipe pengembalian anotasi @Async menjadi void.


Jika ada nilai pengembalian

Anda dapat menggunakan tipe Future, ListenableFuture, CompletableFuture sebagai tipe pengembalian. Bungkus bentuk pengembalian metode asinkron dengan new AsyncResult().


[Masa Depan]

@Service
public class MessageService {

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

// Hasil eksekusi
Mulai Tugas - 1
jayon-1
Mulai Tugas - 2
jayon-2
Mulai Tugas - 3
jayon-3
Mulai Tugas - 4
jayon-4
Mulai Tugas - 5


future.get() berfungsi untuk menunggu hingga hasil permintaan diterima melalui pemblokiran. Karena itu, ia menjadi metode pemblokiran asinkron dan kinerjanya tidak baik. Biasanya, Future tidak digunakan.


[ListenableFuture]

@Service
public class MessageService {

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

// Hasil eksekusi
Mulai Tugas - 1
Mulai Tugas - 3
Mulai Tugas - 2
jayon-1
jayon-2
Mulai Tugas - 5
jayon-3
Mulai Tugas - 4
jayon-4


ListenableFuture memungkinkan Anda untuk memproses tugas secara non-blocking melalui callback. Parameter pertama dari metode addCallback() adalah metode callback penyelesaian tugas, dan parameter kedua adalah metode callback kegagalan tugas. Sebagai catatan, karena ukuran thread core kumpulan thread diatur ke 3, Anda dapat melihat bahwa pesan "Mulai Tugas" pertama kali dicetak 3 kali.



[CompletableFuture]

Meskipun Anda dapat mengimplementasikan logika non-blocking hanya dengan ListenableFuture, jika Anda membutuhkan callback dalam callback, itu dapat menyebabkan kode yang sangat rumit yang disebut callback hell.


Untitled


Tentu saja, kita tidak akan membahas CompletableFuture secara mendetail kali ini, jadi jika Anda ingin mengetahui kode yang menangani callback yang rumit, silakan lihat tautan sumber di bawah.


@Service
public class MessageService {

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


Kejelasan kode lebih baik daripada definisi callback dari ListenableFuture, dan ia melakukan fungsi non-blocking dengan sempurna. Oleh karena itu, disarankan untuk menggunakan CompletableFuture ketika Anda membutuhkan nilai pengembalian saat menggunakan @Async.


Keuntungan @Async

Pengembang dapat menulis metode secara sinkron, dan jika mereka menginginkan metode asinkron, mereka dapat menempelkan anotasi @Async di atas metode tersebut. Oleh karena itu, Anda dapat membuat kode yang mudah dipelihara untuk sinkron dan asinkron.


Hal yang Perlu Diperhatikan @Async

Untuk menggunakan fungsi @Async, Anda harus mendeklarasikan anotasi @EnableAsync. Pada saat itu, jika Anda tidak melakukan pengaturan khusus, ia akan beroperasi dalam mode proxy. Artinya, metode asinkron yang beroperasi dengan anotasi @Async akan mengikuti batasan Spring AOP sebagaimana adanya. Untuk alasan yang lebih spesifik, silakan lihat postingan ini.


  • AOP tidak akan berfungsi meskipun Anda melampirkan @Async ke metode privat.
  • AOP tidak akan berfungsi jika metode dalam objek yang sama saling memanggil.


Sumber

제이온
제이온
제이온
제이온
[Java] Kumpulan Tersinkronisasi vs Kumpulan Bersamaan Dalam Java, kumpulan tersinkronisasi (Vector, Hashtable, Collections.synchronizedXXX) menjamin sinkronisasi dalam lingkungan multithreaded, tetapi dapat menyebabkan penurunan kinerja dan potensi masalah ketika menggunakan beberapa operasi secara bersamaan

25 April 2024

[Spring] Apa itu Filter, Interceptor, dan Argument Resolver? Pelajari tentang konsep dan perbedaan Filter, Interceptor, dan Argument Resolver dalam aplikasi web Spring. Pelajari cara mengimplementasikan masing-masing fitur, kapan harus digunakan, dan bandingkan kelebihan dan kekurangannya melalui kode contoh nyata.

27 April 2024

[Efektif Java] Item 6. Hindari Pembuatan Objek yang Tidak Diperlukan Panduan tentang cara mengurangi pembuatan objek yang tidak diperlukan di Java. String, Boolean, dan objek tak berubah lainnya lebih baik menggunakan literal, dan ekspresi reguler lebih baik menggunakan instance Pattern yang di-cache. Selain itu, autoboxin

28 April 2024

[Concurrency] Operasi Atomik: Memory Fence dan Memory Ordering Postingan blog ini menjelaskan cara mempertimbangkan urutan memori dalam operasi atomik, dan pentingnya opsi Ordering. Ini membahas berbagai opsi Ordering seperti Relaxed, Acquire, Release, AcqRel, SecCst, bersama dengan keuntungan dan kerugian dari masin
곽경직
곽경직
곽경직
곽경직
곽경직

12 April 2024

Cara Rust Mencegah Bug Konkurensi Rust adalah bahasa yang kuat untuk mengatasi tantangan pemrograman konkurensi. Sistem tipenya dan model kepemilikan membuat transfer dan berbagi data antar thread aman. Dengan pola internal mutability seperti Mutex, Channel, dan Atomic, Anda dapat mendefi
곽경직
곽경직
곽경직
곽경직
곽경직

28 Maret 2024

[Non-Major, Survive as a Developer] 14. Ringkasan Konten Wawancara Teknis yang Sering Ditanyakan untuk Pengembang Pemula Panduan persiapan wawancara teknis untuk pengembang pemula. Area memori utama, struktur data, RDBMS dan NoSQL, berorientasi prosedur dan berorientasi objek, overriding dan overloading, algoritma penggantian halaman, proses dan thread, OSI 7 layer, TCP dan
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

3 April 2024

Catatan tentang Kesulitan dalam Pengembangan API Korea Investment & Securities Artikel blog ini ditujukan untuk pengembang yang ingin membuat program perdagangan otomatis menggunakan API Korea Investment & Securities. Artikel ini membahas kesulitan yang dihadapi dalam proses pengembangan, seperti pembukaan akun, ketidakmampuan untuk
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마

23 April 2024

[Next.js] Injeksi Lingkungan Runtime Pelajari cara mengatur variabel lingkungan setelah build di Next.js. Kami memperkenalkan cara menginstal cross-env dan memodifikasi skrip yang mudah diikuti oleh pengguna Windows. Ini dapat digunakan untuk pengaturan variabel lingkungan yang berbeda seper
Sunrabbit
Sunrabbit
Sunrabbit
Sunrabbit

20 Maret 2024

Struktur Dasar Perdagangan Saham Otomatis (Sedang Diperbarui...) Artikel ini menjelaskan secara rinci proses pengembangan program perdagangan saham otomatis langkah demi langkah, mulai dari pembukaan akun hingga persiapan lingkungan pengembangan, integrasi API, desain UI, implementasi logika inti, operasi, dan pengujia
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마

22 April 2024