Java 101: Memahami benang Java, Bahagian 3: Penjadualan benang dan tunggu / maklumkan

Bulan ini, saya meneruskan pengenalan empat bahagian saya ke utas Java dengan memfokuskan pada penjadualan thread, mekanisme tunggu / maklumkan, dan gangguan urutan. Anda akan menyiasat bagaimana JVM atau penjadual utas sistem operasi memilih utas seterusnya untuk pelaksanaan. Seperti yang anda ketahui, keutamaan adalah penting bagi pilihan penjadual utas. Anda akan memeriksa bagaimana utas menunggu sehingga ia menerima pemberitahuan dari utas lain sebelum meneruskan pelaksanaan dan belajar bagaimana menggunakan mekanisme tunggu / maklumkan untuk mengkoordinasikan pelaksanaan dua utas dalam hubungan pengeluar-pengguna. Akhirnya, anda akan belajar bagaimana membangkitkan pramatang sama ada tidur atau menunggu untuk penamatan benang atau tugas lain. Saya juga akan mengajar anda bagaimana utas yang tidak tidur atau menunggu mengesan permintaan gangguan dari utas lain.

Perhatikan bahawa artikel ini (sebahagian daripada arkib JavaWorld) telah dikemas kini dengan senarai kod baru dan kod sumber yang boleh dimuat turun pada bulan Mei 2013.

Memahami benang Java - baca keseluruhan siri

  • Bahagian 1: Memperkenalkan benang dan runnables
  • Bahagian 2: Penyegerakan
  • Bahagian 3: Penjadualan benang, tunggu / maklumkan, dan gangguan urutan
  • Bahagian 4: Kumpulan benang, turun naik, pemboleh ubah benang-tempatan, pemasa, dan kematian benang

Penjadualan benang

Dalam dunia ideal, semua utas program akan mempunyai pemproses mereka sendiri untuk dijalankan. Sehingga tiba masanya ketika komputer mempunyai ribuan atau berjuta-juta pemproses, utas sering mesti berkongsi satu atau lebih pemproses. Sama ada JVM atau sistem operasi platform yang mendasari menentukan cara berkongsi sumber pemproses antara utas - tugas yang dikenali sebagai penjadualan thread . Bahagian JVM atau sistem operasi yang menjalankan penjadualan utas adalah penjadual utas .

Catatan: Untuk mempermudah perbincangan penjadualan thread saya, saya memfokuskan penjadualan thread dalam konteks pemproses tunggal. Anda boleh memperkirakan perbincangan ini kepada beberapa pemproses; Saya menyerahkan tugas itu kepada anda.

Ingat dua perkara penting mengenai penjadualan utas:

  1. Java tidak memaksa VM untuk menjadualkan utas dengan cara tertentu atau mengandungi penjadual utas. Itu menunjukkan penjadualan thread yang bergantung pada platform. Oleh itu, anda mesti berhati-hati semasa menulis program Java yang kelakuannya bergantung pada bagaimana utas dijadualkan dan mesti beroperasi secara konsisten di pelbagai platform.
  2. Nasib baik, semasa menulis program Java, anda perlu memikirkan bagaimana Java menjadualkan utas hanya apabila sekurang-kurangnya salah satu utas program anda banyak menggunakan pemproses untuk jangka masa yang lama dan hasil pertengahan pelaksanaan utas itu terbukti penting. Contohnya, applet mengandungi utas yang menghasilkan gambar secara dinamik. Secara berkala, anda mahu benang lukisan melukis kandungan semasa gambar tersebut supaya pengguna dapat melihat bagaimana gambar itu berkembang. Untuk memastikan bahawa utas pengiraan tidak memonopoli pemproses, pertimbangkan penjadualan utas.

Kaji program yang menghasilkan dua utas intensif pemproses:

Penyenaraian 1. SchedDemo.java

// SchedDemo.java class SchedDemo { public static void main (String [] args) { new CalcThread ("CalcThread A").start (); new CalcThread ("CalcThread B").start (); } } class CalcThread extends Thread { CalcThread (String name) { // Pass name to Thread layer. super (name); } double calcPI () { boolean negative = true; double pi = 0.0; for (int i = 3; i < 100000; i += 2) { if (negative) pi -= (1.0 / i); else pi += (1.0 / i); negative = !negative; } pi += 1.0; pi *= 4.0; return pi; } public void run () { for (int i = 0; i < 5; i++) System.out.println (getName () + ": " + calcPI ()); } }

SchedDemomencipta dua utas yang masing-masing mengira nilai pi (lima kali) dan mencetak setiap hasilnya. Bergantung pada bagaimana pelaksanaan JVM anda menjadualkan utas, anda mungkin melihat output menyerupai yang berikut:

CalcThread A: 3.1415726535897894 CalcThread B: 3.1415726535897894 CalcThread A: 3.1415726535897894 CalcThread A: 3.1415726535897894 CalcThread B: 3.1415726535897894 CalcThread A: 3.1415726535897894 CalcThread A: 3.1415726535897894 CalcThread B: 3.1415726535897894 CalcThread B: 3.1415726535897894 CalcThread B: 3.1415726535897894

Mengikut output di atas, penjadual utas berkongsi pemproses antara kedua utas. Walau bagaimanapun, anda dapat melihat output yang serupa dengan ini:

CalcThread A: 3.1415726535897894 CalcThread A: 3.1415726535897894 CalcThread A: 3.1415726535897894 CalcThread A: 3.1415726535897894 CalcThread A: 3.1415726535897894 CalcThread B: 3.1415726535897894 CalcThread B: 3.1415726535897894 CalcThread B: 3.1415726535897894 CalcThread B: 3.1415726535897894 CalcThread B: 3.1415726535897894

Output di atas menunjukkan penjadual utas lebih menyukai satu utas berbanding yang lain. Dua output di atas menggambarkan dua kategori penjadual utas umum: hijau dan asli. Saya akan meneroka perbezaan tingkah laku mereka di bahagian yang akan datang. Semasa membincangkan setiap kategori, saya merujuk kepada keadaan utas, di mana terdapat empat:

  1. Keadaan awal: Program telah membuat objek utas benang, tetapi utas belum ada kerana start()kaedah objek utas belum dipanggil.
  2. Keadaan yang boleh dijalankan: Ini adalah keadaan lalai benang. Setelah panggilan start()selesai, utas boleh dijalankan sama ada benang itu berjalan atau tidak, iaitu menggunakan pemproses. Walaupun banyak utas boleh dijalankan, hanya satu yang berjalan. Penjadual utas menentukan benang yang dapat dijalankan untuk diberikan kepada pemproses.
  3. Negeri disekat: Apabila benang melaksanakan itu sleep(), wait()atau join()kaedah, apabila thread percubaan untuk membaca data tidak lagi boleh didapati daripada rangkaian, dan apabila menunggu thread untuk memperoleh kunci, thread yang ada di negeri ini disekat: ia tidak berjalan dan tidak dalam kedudukan untuk berlari. (Anda mungkin boleh memikirkan masa-masa lain ketika utas akan menunggu sesuatu berlaku.) Apabila utas yang disekat dibuka, utas itu bergerak ke keadaan yang dapat dijalankan.
  4. Keadaan penamatan: Setelah pelaksanaan meninggalkan run()kaedah utas, utas itu berada dalam keadaan penamatan. Dengan kata lain, benang itu tidak lagi wujud.

Bagaimana penjadual utas memilih utas yang dapat dijalankan untuk dijalankan? Saya mula menjawab soalan itu sambil membincangkan penjadualan benang hijau. Saya menyelesaikan jawapan sambil membincangkan penjadualan benang asli.

Penjadualan benang hijau

Tidak semua sistem operasi, sistem perating Microsoft Windows 3.1 kuno, misalnya, menyokong utas. Untuk sistem seperti itu, Sun Microsystems dapat merancang JVM yang membahagikan satu-satunya utas pelaksanaannya menjadi beberapa utas. JVM (bukan sistem operasi platform yang mendasari) membekalkan logik utas dan mengandungi penjadual utas. Benang JVM adalah utas hijau, atau utas pengguna .

Penjadual utas JVM menjadualkan utas hijau mengikut keutamaan - kepentingan relatif utas, yang anda nyatakan sebagai bilangan bulat dari julat nilai yang ditentukan dengan baik. Biasanya, penjadual utas JVM memilih utas keutamaan tertinggi dan membolehkan utas itu berjalan sehingga ia berhenti atau menyekat. Pada masa itu, penjadual utas memilih utas keutamaan tertinggi seterusnya. Benang itu (biasanya) berjalan sehingga tamat atau tersekat. Sekiranya, semasa utas berjalan, utas dengan keutamaan yang lebih tinggi akan menyahsekat (mungkin masa tidur utas dengan prioriti lebih tinggi tamat), penjadual utas memilih, atau mengganggu, utas keutamaan lebih rendah dan memberikan utas keutamaan lebih tinggi yang tidak disekat kepada pemproses.

Catatan: Benang yang dapat dijalankan dengan keutamaan tertinggi tidak akan selalu berjalan. Inilah keutamaan Spesifikasi Bahasa Jawa :

Setiap utas mempunyai keutamaan. Apabila terdapat persaingan untuk memproses sumber, utas dengan keutamaan yang lebih tinggi biasanya dijalankan lebih baik daripada utas dengan keutamaan yang lebih rendah. Keutamaan seperti itu, bagaimanapun, bukanlah jaminan bahawa utas dengan prioriti tertinggi akan selalu berjalan, dan keutamaan utas tidak dapat digunakan untuk melaksanakan pengecualian bersama.

Pengakuan itu mengatakan banyak mengenai pelaksanaan JVM benang hijau. JVM tersebut tidak mampu membiarkan utas tersekat kerana itu akan mengikat satu-satunya urutan pelaksanaan JVM. Oleh itu, apabila utas mesti menyekat, seperti ketika utas itu membaca data yang lambat sampai dari sebuah fail, JVM mungkin menghentikan pelaksanaan utas dan menggunakan mekanisme pengundian untuk menentukan kapan data tiba. Walaupun utas tetap terhenti, penjadual utas JVM mungkin menjadualkan utas keutamaan yang lebih rendah untuk dijalankan. Katakan data tiba semasa utas keutamaan bawah berjalan. Walaupun utas keutamaan lebih tinggi harus berjalan sebaik sahaja data tiba, itu tidak akan berlaku sehingga JVM seterusnya membuat tinjauan sistem operasi dan menemui kedatangan. Oleh itu, utas keutamaan rendah berjalan walaupun utas keutamaan lebih tinggi harus berjalan.anda perlu risau dengan keadaan ini hanya apabila anda memerlukan tingkah laku masa nyata dari Java. Tetapi kemudian Java bukan sistem operasi masa nyata, jadi mengapa perlu bimbang?

Untuk memahami benang hijau yang dapat dijalankan menjadi benang hijau yang sedang berjalan, pertimbangkan perkara berikut. Katakan aplikasi anda terdiri daripada tiga utas: utas utama yang menjalankan main()kaedah, utas pengiraan, dan utas yang membaca input papan kekunci. Apabila tiada input papan kekunci, utas bacaan akan tersekat. Anggaplah utas bacaan mempunyai keutamaan tertinggi dan utas pengiraan mempunyai keutamaan terendah. (Demi kesederhanaan, anggap juga bahawa tidak ada utas JVM dalaman yang lain tersedia.) Gambar 1 menggambarkan pelaksanaan ketiga utas ini.

Pada masa T0, utas utama mula berjalan. Pada masa T1, utas utama memulakan utas pengiraan. Oleh kerana utas pengiraan mempunyai keutamaan yang lebih rendah daripada utas utama, utas pengiraan menunggu pemproses. Pada masa T2, utas utama memulakan utas membaca. Kerana utas membaca mempunyai keutamaan yang lebih tinggi daripada utas utama, utas utama menunggu pemproses semasa utas membaca berjalan. Pada masa T3, benang membaca menyekat dan utas utama berjalan. Pada masa T4, utas membaca menyekat dan berjalan; utas utamanya menunggu. Akhirnya, pada masa T5, benang membaca menyekat dan utas utama berjalan. Pergantian pelaksanaan ini antara bacaan dan utas utama berterusan sepanjang program dijalankan. Benang pengiraan tidak pernah berjalan kerana mempunyai keutamaan terendah dan dengan itu kelaparan untuk perhatian pemproseskeadaan yang dikenali sebagaikebuluran pemproses .

Kita dapat mengubah senario ini dengan memberikan utas pengiraan keutamaan yang sama dengan utas utama. Gambar 2 menunjukkan hasilnya, bermula dengan masa T2. (Sebelum T2, Gambar 2 sama dengan Gambar 1.)

Pada masa T2, utas bacaan berjalan sementara utas utama dan pengiraan menunggu pemproses. Pada waktu T3, utas pembacaan menyekat dan utas pengiraan berjalan, kerana utas utama berlari tepat sebelum utas membaca. Pada masa T4, utas membaca menyekat dan berjalan; utas utama dan pengiraan menunggu. Pada waktu T5, utas bacaan menyekat dan utas utama berjalan, kerana benang pengiraan berjalan tepat sebelum utas membaca. Pergantian pelaksanaan ini antara utas utama dan utas pengiraan berterusan selagi program berjalan dan bergantung pada urutan utamanya yang lebih tinggi berjalan dan disekat.

We must consider one last item in green thread scheduling. What happens when a lower-priority thread holds a lock that a higher-priority thread requires? The higher-priority thread blocks because it cannot get the lock, which implies that the higher-priority thread effectively has the same priority as the lower-priority thread. For example, a priority 6 thread attempts to acquire a lock that a priority 3 thread holds. Because the priority 6 thread must wait until it can acquire the lock, the priority 6 thread ends up with a 3 priority—a phenomenon known as priority inversion.

Pembalikan keutamaan dapat melambatkan pelaksanaan utas keutamaan yang lebih tinggi. Sebagai contoh, anggap anda mempunyai tiga utas dengan keutamaan 3, 4, dan 9. Utas 3 utas berjalan dan utas yang lain disekat. Anggapkan bahawa utas keutamaan 3 memegang kunci, dan utas keutamaan 4 membuka blokir. Utas keutamaan 4 menjadi utas yang sedang berjalan. Oleh kerana utas keutamaan 9 memerlukan kunci, ia terus menunggu sehingga utas keutamaan 3 melepaskan kunci. Walau bagaimanapun, utas keutamaan 3 tidak dapat melepaskan kunci sehingga utas keutamaan 4 menyekat atau berhenti. Hasilnya, utas keutamaan 9 menunda pelaksanaannya.