Kapan Sebaiknya Memakai Virtual Thread di Java
Virtual thread, yang menjadi fitur final di Java 21, memungkinkan pembuatan thread dalam jumlah besar dengan biaya overhead rendah. Keputusan memakainya bergantung pada jenis beban kerja. Artikel ini membandingkan virtual thread dengan platform thread dan thread pool, lalu memberi panduan kapan memakai virtual thread di Java dan kapan tetap memakai pendekatan konvensional.
Virtual Thread vs Platform Thread
Platform thread dipetakan langsung ke thread sistem operasi. Jumlahnya dibatasi sumber daya OS, dan setiap thread memakan memori cukup besar untuk stack. Virtual thread dikelola oleh JVM. JVM menjadwalkan virtual thread di atas sejumlah kecil platform thread (carrier thread). Saat virtual thread melakukan operasi blocking seperti I/O, ia di-unmount dari carrier thread sehingga carrier thread dapat menjalankan virtual thread lain. Satu platform thread dapat melayani banyak virtual thread secara bergantian.
Karena itu, virtual thread efisien untuk beban kerja yang banyak menunggu, seperti permintaan HTTP ke layanan lain, query database, atau operasi file. Platform thread lebih cocok untuk tugas intensif komputasi karena virtual thread tidak memberi keuntungan pada CPU-bound task; overhead pergantian virtual thread bahkan bisa sedikit memperlambat eksekusi.
Kapan Virtual Thread Menjadi Pilihan Tepat
Gunakan virtual thread ketika aplikasi memiliki banyak tugas konkuren yang sebagian besar waktunya dihabiskan untuk menunggu. Contohnya server HTTP yang melayani ribuan koneksi simultan, klien yang memanggil banyak API secara paralel, atau proses yang membaca dan menulis ke sistem eksternal. Dengan virtual thread, Anda tidak perlu mengelola thread pool berukuran tetap untuk menghindari kehabisan thread. Cukup buat virtual thread per tugas, dan JVM mengurus penjadwalannya.
Virtual thread juga menyederhanakan model pemrograman. Anda bisa menulis kode blocking gaya lama tanpa beralih ke gaya reaktif yang kompleks. Kode berikut membuat 10.000 virtual thread yang masing-masing melakukan panggilan I/O:
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 10_000; i++) {
executor.submit(() -> {
// simulasi operasi I/O
Thread.sleep(1000);
return null;
});
}
} // executor ditutup dan menunggu semua tugas selesai
Kode di atas tidak memerlukan konfigurasi pool yang rumit. Anda tidak perlu menentukan ukuran inti, antrean, atau kebijakan penolakan. Ini mengurangi risiko kesalahan konfigurasi yang sering terjadi pada thread pool tradisional, seperti yang dibahas dalam artikel Mengelola Thread Pool di Java: Panduan Langkah demi Langkah.
Kapan Sebaiknya Tetap Memakai Platform Thread atau Thread Pool
Virtual thread bukan pengganti universal untuk platform thread. Ada beberapa situasi di mana platform thread atau thread pool tetap lebih sesuai. Untuk tugas intensif komputasi seperti pemrosesan gambar, kriptografi, atau kompresi data, virtual thread tidak akan meningkatkan throughput karena CPU menjadi bottleneck, dan penggunaannya dapat menambah overhead penjadwalan. Ketika Anda membutuhkan kontrol ketat atas jumlah thread yang berjalan bersamaan untuk membatasi penggunaan sumber daya, misalnya pada aplikasi yang harus mematuhi kuota sumber daya tertentu, thread pool dengan ukuran tetap lebih mudah diprediksi.
Jika kode Anda menggunakan synchronized block atau metode yang menahan lock dalam waktu lama, virtual thread dapat mengalami pinning, yaitu kondisi ketika virtual thread terikat pada carrier thread dan tidak dapat di-unmount. Hal ini mengurangi efisiensi dan dapat menyebabkan kelaparan carrier thread. Untuk menghindari pinning, gunakan ReentrantLock sebagai ganti synchronized pada bagian kritis, atau pastikan operasi di dalam synchronized tidak melakukan blocking I/O. Jika arsitektur Anda banyak bergantung pada synchronized, tetap pakai platform thread sampai kode disesuaikan.
Untuk aplikasi yang membutuhkan ThreadLocal dengan data besar per thread, virtual thread tetap menyimpan ThreadLocal, dan jumlah virtual thread yang sangat besar dapat menghabiskan memori. Bersihkan ThreadLocal setelah digunakan atau beralih ke pendekatan lain.
Memadukan Virtual Thread dengan Connection Pool
Meskipun virtual thread memungkinkan pembuatan thread dalam jumlah besar, sumber daya eksternal seperti koneksi database tetap terbatas. Connection pool mengatur jumlah koneksi yang dapat dibuka ke database. Jika Anda membuat virtual thread tanpa batas dan masing-masing mencoba mengambil koneksi dari pool, antrean terjadi di pool, bukan di thread. Virtual thread tetap efisien karena thread yang menunggu koneksi di-unmount, tetapi Anda perlu memastikan ukuran pool sesuai dengan kapasitas database. Konfigurasi connection pool yang tepat tetap penting, seperti dijelaskan dalam Panduan Konfigurasi Connection Pool di Java.
Menggunakan virtual thread bersama connection pool yang terlalu kecil dapat menyebabkan waktu tunggu lama. Pantau metrik seperti waktu tunggu koneksi dan jumlah virtual thread yang aktif. Jika perlu, sesuaikan ukuran pool atau batasi jumlah virtual thread yang mengakses database secara bersamaan dengan semaphore.
Dampak pada Arsitektur Aplikasi
Dengan virtual thread, arsitektur aplikasi Java dapat disederhanakan. Anda tidak perlu memisahkan thread pool untuk operasi yang berbeda atau mengimplementasikan backpressure yang rumit. Tetap perlu ada pembatasan pada sumber daya langka, seperti koneksi database, koneksi HTTP ke layanan eksternal, atau file handle. Virtual thread memindahkan kompleksitas dari manajemen thread ke manajemen sumber daya lain.
Untuk aplikasi intranet yang melayani banyak pengguna dengan operasi baca-tulis database, virtual thread dapat meningkatkan skalabilitas tanpa mengubah model pemrograman. Beberapa driver JDBC mungkin belum sepenuhnya kompatibel dengan virtual thread, terutama yang menggunakan synchronized di dalamnya. Uji dengan beban realistis sebelum menerapkan di produksi. Pastikan juga infrastruktur jaringan mendukung. Kestabilan koneksi, seperti yang dibahas dalam Mengukur Jitter dan Stabilitas Koneksi, Bukan Cuma Ping, mempengaruhi kinerja aplikasi yang banyak melakukan panggilan jaringan. Virtual thread tidak menghilangkan latensi jaringan, tetapi membuat penanganannya lebih efisien.
Ringkasan
Virtual thread di Java cocok untuk beban kerja yang banyak menunggu, seperti server HTTP dan klien API, karena memungkinkan konkurensi tinggi dengan model pemrograman blocking yang sederhana. Untuk tugas intensif komputasi, kontrol thread yang ketat, atau kode yang banyak menggunakan synchronized, platform thread atau thread pool masih lebih tepat. Pahami karakteristik aplikasi Anda dan uji dengan beban yang representatif sebelum memutuskan.
Pertanyaan yang Sering Diajukan
Apakah virtual thread bisa menggantikan thread pool sepenuhnya?
Tidak selalu. Virtual thread unggul untuk tugas yang sering blocking, tetapi thread pool masih berguna untuk membatasi konkurensi pada sumber daya tertentu atau untuk tugas CPU-bound yang membutuhkan kontrol jumlah thread.
Apakah virtual thread aman digunakan dengan synchronized?
Virtual thread dapat digunakan dengan synchronized, tetapi jika terjadi blocking di dalam blok synchronized, virtual thread dapat ter-pin pada carrier thread dan mengurangi efisiensi. Gunakan ReentrantLock untuk operasi yang berpotensi blocking.
Bagaimana cara membatasi jumlah virtual thread yang mengakses database?
Gunakan semaphore atau struktur pembatas lainnya untuk mengontrol berapa banyak virtual thread yang boleh mengambil koneksi dari pool secara bersamaan. Ukuran pool database tetap menjadi faktor penentu utama.









