Model Threading di Java

1. Perkenalan

Seringkali dalam aplikasi kita, kita harus dapat melakukan banyak hal pada waktu yang bersamaan. Kami dapat mencapai ini dengan beberapa cara, tetapi kunci di antaranya adalah menerapkan multitasking dalam beberapa bentuk.

Multi-tasking berarti menjalankan banyak tugas secara bersamaan , di mana setiap tugas sedang melakukan tugasnya. Semua tugas ini biasanya berjalan pada waktu yang sama, membaca dan menulis memori yang sama dan berinteraksi dengan sumber daya yang sama, tetapi melakukan hal yang berbeda.

2. Utas Asli

Cara standar untuk mengimplementasikan multi-tasking di Java adalah dengan menggunakan thread . Threading biasanya didukung hingga ke sistem operasi. Kami menyebut utas yang bekerja pada level ini sebagai “utas asli”.

Sistem operasi memiliki beberapa kemampuan dengan threading yang sering tidak tersedia untuk aplikasi kita, hanya karena seberapa dekat itu dengan perangkat keras yang mendasarinya. Ini berarti bahwa menjalankan utas asli biasanya lebih efisien. Utas ini secara langsung memetakan ke utas eksekusi pada CPU komputer - dan sistem operasi mengelola pemetaan utas ke inti CPU.

Model threading standar di Java, yang mencakup semua bahasa JVM, menggunakan utas asli . Ini telah terjadi sejak Java 1.2 dan merupakan kasus terlepas dari sistem yang mendasari JVM berjalan.

Ini berarti bahwa setiap kali kami menggunakan salah satu mekanisme penguliran standar di Java, kami menggunakan utas asli. Ini termasuk java.lang.Thread , java.util.concurrent.Executor , java.util.concurrent.ExecutorService , dan seterusnya.

3. Benang Hijau

Dalam rekayasa perangkat lunak, salah satu alternatif utas asli adalah utas hijau . Di sinilah kami menggunakan utas, tetapi utas tidak langsung memetakan ke utas sistem operasi. Sebaliknya, arsitektur yang mendasari mengelola utas itu sendiri dan mengelola bagaimana peta ini dipetakan ke utas sistem operasi.

Biasanya ini bekerja dengan menjalankan beberapa utas asli dan kemudian mengalokasikan utas hijau ke utas asli ini untuk dieksekusi . Sistem kemudian dapat memilih utas hijau mana yang aktif pada waktu tertentu, dan utas asli mana tempat mereka aktif.

Ini terdengar sangat rumit, dan memang begitu. Tetapi ini adalah komplikasi yang biasanya tidak perlu kita pedulikan. Arsitektur yang mendasari menangani semua ini, dan kita bisa menggunakannya seolah-olah itu adalah model threading asli.

Jadi mengapa kita melakukan ini? Utas asli sangat efisien untuk dijalankan, tetapi membutuhkan biaya tinggi untuk memulai dan menghentikannya. Benang hijau membantu menghindari biaya ini dan memberikan arsitektur yang lebih fleksibel. Jika kami menggunakan utas yang berjalan relatif lama, utas asli sangat efisien. Untuk pekerjaan yang berumur sangat pendek, biaya memulainya bisa lebih besar daripada manfaat penggunaannya . Dalam kasus ini, benang hijau bisa menjadi lebih efisien.

Sayangnya, Java tidak memiliki dukungan bawaan untuk benang hijau.

Versi paling awal menggunakan utas hijau, bukan utas asli sebagai model utas standar. Ini berubah di Java 1.2, dan tidak ada dukungan untuk itu di level JVM sejak itu.

Mengimplementasikan benang hijau di perpustakaan juga menantang karena mereka membutuhkan dukungan tingkat yang sangat rendah untuk bekerja dengan baik. Dengan demikian, alternatif yang umum digunakan adalah serat.

4. Serat

Serat adalah bentuk alternatif dari multi-threading dan mirip dengan benang hijau . Dalam kedua kasus tersebut, kami tidak menggunakan utas asli dan sebagai gantinya menggunakan kontrol sistem yang mendasari yang berjalan kapan saja. Perbedaan besar antara benang hijau dan serat terletak pada tingkat kendali, dan khususnya siapa yang mengendalikan.

Benang hijau adalah bentuk multitasking preemptive. Ini berarti bahwa arsitektur yang mendasari sepenuhnya bertanggung jawab untuk memutuskan utas mana yang sedang dijalankan pada waktu tertentu.

Ini berarti bahwa semua masalah biasa dari threading berlaku, di mana kami tidak tahu apa-apa tentang urutan utas yang dieksekusi, atau mana yang akan dijalankan pada saat yang sama. Ini juga berarti bahwa sistem yang mendasarinya harus dapat menjeda dan memulai ulang kode kami kapan saja, berpotensi di tengah metode atau bahkan pernyataan.

Serat adalah bentuk multitasking kooperatif, yang berarti bahwa thread yang sedang berjalan akan terus berjalan hingga memberi sinyal bahwa ia dapat menghasilkan yang lain . Ini berarti bahwa merupakan tanggung jawab kami agar serat bekerja sama satu sama lain. Ini menempatkan kita dalam kendali langsung atas kapan serat dapat menjeda eksekusi, alih-alih sistem yang memutuskannya untuk kita.

Ini juga berarti kita perlu menulis kode kita dengan cara yang memungkinkan untuk ini. Jika tidak, itu tidak akan berhasil. Jika kode kita tidak memiliki titik interupsi, maka kita mungkin juga tidak menggunakan fiber sama sekali.

Java saat ini tidak memiliki dukungan bawaan untuk fiber. Beberapa perpustakaan ada yang dapat memperkenalkan ini ke aplikasi kita, termasuk tetapi tidak terbatas pada:

4.1. Quasar

Quasar adalah pustaka Java yang bekerja dengan baik dengan Java dan Kotlin murni serta memiliki versi alternatif yang bekerja dengan Clojure.

Ia bekerja dengan memiliki agen Java yang perlu dijalankan bersama aplikasi, dan agen ini bertanggung jawab untuk mengelola serat dan memastikan bahwa mereka bekerja sama dengan benar. Penggunaan agen Java berarti tidak diperlukan langkah build khusus.

Quasar juga membutuhkan Java 11 untuk bekerja dengan benar sehingga mungkin membatasi aplikasi yang dapat menggunakannya. Versi yang lebih lama dapat digunakan di Java 8, tetapi ini tidak didukung secara aktif.

4.2. Kilim

Kilim adalah pustaka Java yang menawarkan fungsionalitas yang sangat mirip dengan Quasar tetapi melakukannya dengan menggunakan bytecode weaving alih-alih agen Java . Ini berarti dapat berfungsi di lebih banyak tempat, tetapi membuat proses pembuatan menjadi lebih rumit.

Kilim bekerja dengan Java 7 dan yang lebih baru dan akan bekerja dengan benar bahkan dalam skenario di mana agen Java bukan merupakan pilihan. Misalnya, jika instrumen lain sudah digunakan untuk instrumentasi atau pemantauan.

4.3. Project Loom

Project Loom adalah eksperimen oleh proyek OpenJDK untuk menambahkan serat ke JVM itu sendiri, bukan sebagai pustaka pengaya . Ini akan memberi kita keunggulan serat dibandingkan benang. Dengan mengimplementasikannya di JVM secara langsung, ini dapat membantu menghindari komplikasi yang diperkenalkan oleh agen Java dan bytecode.

Tidak ada jadwal rilis saat ini untuk Project Loom, tetapi kami dapat mengunduh binari akses awal sekarang untuk melihat bagaimana perkembangannya. Namun, karena ini masih sangat awal, kami perlu berhati-hati mengandalkan ini untuk kode produksi apa pun.

5. Rutinitas Bersama

Rutinitas bersama adalah alternatif untuk penguliran dan serat. Kita dapat menganggap rutinitas bersama sebagai serat tanpa bentuk penjadwalan apa pun . Alih-alih sistem yang mendasari memutuskan tugas mana yang dilakukan kapan saja, kode kami melakukannya secara langsung.

Umumnya, kita menulis rutinitas bersama sehingga menghasilkan pada titik tertentu alirannya. Ini dapat dilihat sebagai titik jeda dalam fungsi kita, di mana ia akan berhenti bekerja dan berpotensi mengeluarkan beberapa hasil antara. Ketika kami menghasilkan, kami kemudian dihentikan sampai kode panggilan memutuskan untuk memulai kembali kami untuk alasan apa pun. Ini berarti bahwa kode panggilan kami mengontrol penjadwalan kapan ini akan dijalankan.

Kotlin memiliki dukungan native untuk rutinitas bersama yang dibangun ke dalam pustaka standarnya. Ada beberapa pustaka Java lain yang dapat kita gunakan untuk mengimplementasikannya juga jika diinginkan.

6. Kesimpulan

Kami telah melihat beberapa alternatif berbeda untuk multi-tasking dalam kode kami, mulai dari utas asli tradisional hingga beberapa alternatif yang sangat ringan. Mengapa tidak mencobanya saat aplikasi membutuhkan konkurensi?