Halo! Pertama-tama, selamat: Anda telah mencapai topik Multithreading di Java! Ini adalah pencapaian yang serius, jalan yang harus ditempuh masih panjang. Namun bersiaplah: ini adalah salah satu topik tersulit dalam kursus ini. Dan intinya bukanlah kelas yang rumit atau banyak metode yang digunakan di sini: sebaliknya, bahkan tidak ada dua lusin. Lebih dari itu, Anda perlu sedikit mengubah pemikiran Anda. Sebelumnya, program Anda dijalankan secara berurutan. Beberapa baris kode mengikuti baris lainnya, beberapa metode mengikuti baris lainnya, dan secara keseluruhan semuanya jelas. Pertama, hitung sesuatu, lalu tampilkan hasilnya di konsol, lalu hentikan program. Untuk memahami multithreading, yang terbaik adalah berpikir dalam kerangka konkurensi. Mari kita mulai dengan sesuatu yang sangat sederhana :) Multithreading di Java: esensi, kelebihan dan kekurangan umum - 1Bayangkan keluarga Anda berpindah dari satu rumah ke rumah lainnya. Bagian penting dari perpindahan adalah mengemas buku Anda. Anda telah mengumpulkan banyak buku, dan Anda harus memasukkannya ke dalam kotak. Sekarang hanya kamu yang bebas. Ibu sedang menyiapkan makanan, kakak sedang mengumpulkan pakaian, dan adik sudah pergi ke toko. Setidaknya Anda dapat mengelolanya sendiri, dan, cepat atau lambat, Anda bahkan akan menyelesaikan tugas itu sendiri, tetapi itu akan memakan banyak waktu. Namun, dalam 20 menit adikmu akan kembali dari toko, dan dia tidak punya pekerjaan lain. Jadi dia bisa bergabung denganmu. Tugasnya tetap sama: memasukkan buku ke dalam kotak. Itu hanya berjalan dua kali lebih cepat. Mengapa? Karena pekerjaan dilakukan secara paralel. Dua “utas” yang berbeda (Anda dan saudara perempuan Anda) secara bersamaan melakukan tugas yang sama dan, jika tidak ada perubahan, perbedaan waktu akan sangat besar dibandingkan dengan situasi di mana Anda melakukan semuanya sendirian. Jika kakakmu segera menyelesaikan tugasnya, dia bisa membantumu, dan segalanya akan berjalan lebih cepat.

Masalah yang dipecahkan multithreading di Java

Pada dasarnya, multithreading Java diciptakan untuk memecahkan dua masalah utama:
  1. Lakukan beberapa tindakan secara bersamaan.

    Dalam contoh di atas, berbagai thread (yaitu anggota keluarga) melakukan beberapa tindakan secara paralel: mencuci piring, pergi ke toko, melipat barang.

    Contoh yang lebih “programmer” dapat diberikan. Bayangkan Anda memiliki sebuah program dengan antarmuka pengguna. Ketika tombol Lanjutkan diklik, beberapa perhitungan akan terjadi dalam program, dan pengguna akan melihat layar antarmuka berikut. Jika tindakan ini dilakukan secara berurutan, setelah mengklik tombol "Lanjutkan", program akan terhenti begitu saja. Pengguna akan melihat layar yang sama dengan tombol “Lanjutkan” hingga semua perhitungan internal selesai dan program mencapai bagian di mana antarmuka akan mulai digambar.

    Baiklah, mari kita tunggu beberapa menit!

    Multithreading di Java: esensi, kelebihan dan kekurangan umum - 3

    Kita juga dapat membuat ulang program kita, atau, seperti yang dikatakan oleh programmer, “memparalelkan.” Biarkan perhitungan yang diperlukan dilakukan di satu thread, dan rendering antarmuka di thread lain. Sebagian besar komputer memiliki sumber daya yang cukup untuk ini. Dalam hal ini, program tidak akan "bodoh", dan pengguna akan dengan tenang berpindah antar layar antarmuka tanpa mengkhawatirkan apa yang terjadi di dalamnya. Itu tidak mengganggu :)

  2. Mempercepat perhitungan.

    Semuanya jauh lebih sederhana di sini. Jika prosesor kami memiliki beberapa inti, dan sebagian besar prosesor sekarang multi-inti, daftar tugas kami dapat diselesaikan secara paralel dengan beberapa inti. Jelasnya, jika kita perlu menyelesaikan 1000 masalah dan masing-masing masalah diselesaikan dalam satu detik, satu inti akan menyelesaikan daftar tersebut dalam 1000 detik, dua inti dalam 500 detik, tiga inti hanya dalam waktu 333 detik, dan seterusnya.

Namun, seperti yang telah Anda baca di kuliah, sistem modern sangat cerdas, dan bahkan pada satu inti komputasi mereka mampu menerapkan paralelisme, atau paralelisme semu, ketika tugas dilakukan secara bergantian. Mari beralih dari hal umum ke hal khusus dan mengenal kelas utama di perpustakaan Java yang terkait dengan multithreading - java.lang.Thread. Sebenarnya, thread di Java diwakili oleh instance kelas Thread. Artinya, untuk membuat dan menjalankan 10 thread, Anda memerlukan 10 objek kelas ini. Mari kita tulis contoh paling sederhana:
public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("I'm Thread! My name is " + getName());
   }
}
Untuk membuat dan meluncurkan thread, kita perlu membuat kelas dan mewarisinya dari java.lang. Threaddan ganti metode di dalamnya run(). Yang terakhir ini sangat penting. Di dalam metode itulah run()kita menentukan logika yang harus dijalankan oleh thread kita. Sekarang, jika kita membuat sebuah instance MyFirstThreaddan menjalankannya, metode ini run()akan mencetak baris dengan namanya ke konsol: metode ini getName()akan mencetak nama “sistem” dari thread, yang ditetapkan secara otomatis. Meskipun sebenarnya mengapa “jika”? Ayo buat dan uji!
public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {

           MyFirstThread thread = new MyFirstThread();
           thread.start();
       }
   }
}
Keluaran konsol: Saya Thread! Nama saya Thread-2 Saya Thread! Nama saya Thread-1 Saya Thread! Nama saya Thread-0 Saya Thread! Nama saya Thread-3 Saya Thread! Nama saya Thread-6 Saya Thread! Nama saya Thread-7 Saya Thread! Nama saya Thread-4 Saya Thread! Nama saya Thread-5 Saya Thread! Nama saya Thread-9 Saya Thread! Nama saya Thread-8 Kami membuat 10 thread (objek) MyFirstThreadyang diwarisi Threaddan meluncurkannya dengan memanggil metode objek start(). Setelah memanggil suatu metode , start()metodenya mulai bekerja run(), dan logika yang tertulis di dalamnya dijalankan. Harap diperhatikan: nama thread tidak berurutan. Cukup aneh, kenapa tidak dieksekusi secara bergantian: Thread-0, Thread-1, Thread-2dan seterusnya? Ini adalah contoh ketika pemikiran standar dan “berurutan” tidak akan berhasil. Faktanya adalah dalam hal ini kami hanya mengeluarkan perintah untuk membuat dan meluncurkan 10 thread. Urutan peluncurannya ditentukan oleh penjadwal thread: mekanisme khusus di dalam sistem operasi. Bagaimana sebenarnya strukturnya dan berdasarkan prinsip apa ia mengambil keputusan adalah topik yang sangat kompleks, dan kami tidak akan mendalaminya sekarang. Hal utama yang perlu diingat adalah programmer tidak dapat mengontrol urutan eksekusi thread. Untuk menyadari keseriusan situasi ini, coba jalankan metode main()dari contoh di atas beberapa kali lagi. Keluaran konsol kedua: Saya Thread! Nama saya Thread-0 Saya Thread! Nama saya Thread-4 Saya Thread! Nama saya Thread-3 Saya Thread! Nama saya Thread-2 Saya Thread! Nama saya Thread-1 Saya Thread! Nama saya Thread-5 Saya Thread! Nama saya Thread-6 Saya Thread! Nama saya Thread-8 Saya Thread! Nama saya Thread-9 Saya Thread! Nama saya Thread-7 Output konsol ketiga: Saya Thread! Nama saya Thread-0 Saya Thread! Nama saya Thread-3 Saya Thread! Nama saya Thread-1 Saya Thread! Nama saya Thread-2 Saya Thread! Nama saya Thread-6 Saya Thread! Nama saya Thread-4 Saya Thread! Nama saya Thread-9 Saya Thread! Nama saya Thread-5 Saya Thread! Nama saya Thread-7 Saya Thread! Nama saya Thread-8

Masalah yang ditimbulkan oleh multithreading

Dalam contoh buku, Anda melihat bahwa multithreading memecahkan masalah yang cukup penting, dan penggunaannya mempercepat kerja program kami. Dalam banyak kasus - berkali-kali. Namun bukan tanpa alasan bahwa multithreading dianggap sebagai topik yang kompleks. Lagi pula, jika digunakan secara tidak benar, hal itu malah menimbulkan masalah, bukan menyelesaikannya. Saat saya mengatakan “menciptakan masalah”, yang saya maksud bukanlah sesuatu yang abstrak. Ada dua masalah spesifik yang dapat disebabkan oleh multithreading: kebuntuan dan kondisi balapan. Deadlock adalah situasi di mana beberapa thread menunggu sumber daya ditempati satu sama lain, dan tidak ada satupun yang dapat melanjutkan eksekusi. Kita akan membicarakannya lebih lanjut pada kuliah berikutnya, tapi untuk saat ini contoh ini sudah cukup: Multithreading di Java: esensi, kelebihan dan kekurangan umum - 4 Bayangkan thread-1 bekerja dengan beberapa Objek-1, dan thread-2 bekerja dengan Objek-2. Programnya ditulis seperti ini:
  1. Thread-1 akan berhenti bekerja dengan Objek-1 dan beralih ke Objek-2 segera setelah Thread-2 berhenti bekerja dengan Objek 2 dan beralih ke Objek-1.
  2. Thread-2 akan berhenti bekerja dengan Objek-2 dan beralih ke Objek-1 segera setelah Thread-1 berhenti bekerja dengan Objek 1 dan beralih ke Objek-2.
Bahkan tanpa pengetahuan mendalam tentang multithreading, Anda dapat dengan mudah memahami bahwa tidak ada hasil apa pun. Utasnya tidak akan pernah berpindah tempat dan akan menunggu satu sama lain selamanya. Kesalahannya tampak jelas, namun kenyataannya tidak. Anda dapat dengan mudah mengizinkannya masuk ke dalam program. Kita akan melihat contoh kode yang menyebabkan kebuntuan pada kuliah berikut. Ngomong-ngomong, Quora punya contoh kehidupan nyata yang menjelaskan apa itu kebuntuan . “Di beberapa negara bagian di India, mereka tidak akan menjual lahan pertanian kepada Anda kecuali Anda terdaftar sebagai petani. Namun, Anda tidak akan terdaftar sebagai petani jika Anda tidak memiliki lahan pertanian.” Hebat, apa yang bisa saya katakan! :) Sekarang tentang kondisi balapan – keadaan balapan. Multithreading di Java: esensi, kelebihan dan kekurangan umum - 5Kondisi balapan adalah cacat desain dalam sistem atau aplikasi multi-thread di mana pengoperasian sistem atau aplikasi bergantung pada urutan bagian kode yang dieksekusi. Ingat contoh dengan thread yang sedang berjalan:
public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("Выполнен поток " + getName());
   }
}

public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {

           MyFirstThread thread = new MyFirstThread();
           thread.start();
       }
   }
}
Sekarang bayangkan program tersebut bertanggung jawab atas pengoperasian robot yang menyiapkan makanan! Thread-0 mengeluarkan telur dari lemari es. Aliran 1 menyalakan kompor. Stream-2 mengeluarkan penggorengan dan menaruhnya di atas kompor. Aliran 3 menyalakan api di atas kompor. Stream 4 menuangkan minyak ke dalam wajan. Stream 5 memecahkan telur dan menuangkannya ke dalam penggorengan. Stream 6 melempar cangkangnya ke tempat sampah. Stream-7 mengeluarkan telur orak-arik yang sudah jadi dari api. Potok-8 menaruh telur orak-arik di piring. Aliran 9 mencuci piring. Lihatlah hasil program kita: Thread-0 dieksekusi Thread-2 thread dieksekusi Thread-1 thread dieksekusi Thread-4 thread dieksekusi Thread-9 thread dieksekusi Thread-5 thread dieksekusi Thread-8 thread dieksekusi Thread-7 thread dieksekusi Thread-7 thread dieksekusi -3 Thread-6 thread dieksekusi Apakah skripnya menyenangkan? :) Dan semua itu karena pengoperasian program kita bergantung pada urutan eksekusi thread. Sedikit saja pelanggaran urutannya, dapur kita berubah menjadi neraka, dan robot yang menjadi gila menghancurkan segala sesuatu di sekitarnya. Ini juga merupakan masalah umum dalam pemrograman multithread, yang akan Anda dengar lebih dari sekali. Di akhir kuliah, saya ingin merekomendasikan Anda sebuah buku tentang multithreading.
Multithreading di Java: esensi, kelebihan dan kekurangan umum - 6
“Java Concurrency in Practice” ditulis pada tahun 2006, namun masih tetap relevan. Ini mencakup pemrograman multithread di Java, mulai dari dasar dan diakhiri dengan daftar kesalahan dan antipola yang paling umum. Jika Anda memutuskan untuk menjadi ahli pemrograman multithread, buku ini wajib dibaca. Sampai jumpa di perkuliahan selanjutnya! :)