Java 101: Memahami thread Java, Bagian 4: Grup thread, volatilitas, dan variabel thread-lokal

Java 101 bulan ini mengakhiri rangkaian thread dengan berfokus pada grup thread, volatilitas, variabel thread-lokal, timer, dan ThreadDeathkelas.

Memahami utas Java - baca keseluruhan seri

  • Bagian 1: Memperkenalkan utas dan runnable
  • Bagian 2: Sinkronisasi benang
  • Bagian 3: Penjadwalan thread, tunggu / beri tahu, dan gangguan thread
  • Bagian 4: Grup thread, volatilitas, variabel thread-lokal, timer, dan kematian thread

Grup benang

Dalam program server jaringan, satu utas menunggu dan menerima permintaan dari program klien untuk dieksekusi, misalnya, transaksi database atau kalkulasi kompleks. Utas biasanya membuat utas baru untuk menangani permintaan. Bergantung pada volume permintaan, banyak utas berbeda mungkin hadir secara bersamaan, mempersulit manajemen utas. Untuk menyederhanakan manajemen utas, program mengatur utas mereka dengan grup utas - java.lang.ThreadGroupobjek yang mengelompokkan objek utas Thread(dan Threadsubkelas) terkait. Misalnya, program Anda dapat menggunakan ThreadGroupuntuk mengelompokkan semua utas pencetakan ke dalam satu grup.

Catatan: Agar diskusi tetap sederhana, saya merujuk ke grup utas seolah-olah mereka mengatur utas. Pada kenyataannya, grup utas mengatur Thread(dan Threadsubkelas) objek yang terkait dengan utas.

Java mengharuskan setiap utas dan setiap grup utas — simpan grup utas akar, system—untuk bergabung dengan beberapa grup utas lainnya. Pengaturan tersebut mengarah ke struktur grup thread hierarkis, yang diilustrasikan gambar di bawah ini dalam konteks aplikasi.

Di bagian atas struktur gambar adalah systemgrup utas. Grup yang dibuat systemJVM mengatur utas JVM yang menangani penyelesaian objek dan tugas sistem lainnya, dan berfungsi sebagai grup utas akar dari struktur grup utas hierarki aplikasi. Tepat di bawah systemini adalah maingrup utas yang dibuat JVM , yang merupakan systemgrup subjudul (subgrup, singkatnya). mainberisi setidaknya satu utas — utas utama buatan JVM yang menjalankan instruksi kode byte dalam main()metode tersebut.

Di bawah maingrup berada subgroup 1dan subgroup 2subkelompok, subkelompok yang dibuat oleh aplikasi (yang dibuat oleh aplikasi gambar). Selanjutnya, subgroup 1kelompok tiga benang aplikasi-dibuat: thread 1, thread 2, dan thread 3. Sebaliknya, subgroup 2kelompok satu aplikasi yang dibuat thread: my thread.

Sekarang setelah Anda mengetahui dasar-dasarnya, mari mulai membuat grup utas.

Buat grup utas dan kaitkan utas dengan grup itu

The ThreadGroupkelas dokumentasi SDK mengungkapkan dua konstruktor: ThreadGroup(String name)dan ThreadGroup(ThreadGroup parent, String name). Kedua konstruktor membuat grup utas dan memberinya nama, seperti yang nameditentukan oleh parameter. Konstruktor berbeda dalam memilih grup utas mana yang berfungsi sebagai induk grup utas yang baru dibuat. Setiap grup utas, kecuali system, harus memiliki grup utas induk. Sebab ThreadGroup(String name), induk adalah grup utas dari utas yang memanggil ThreadGroup(String name). Sebagai contoh, jika utas utama memanggil ThreadGroup(String name), grup utas yang baru dibuat memiliki grup utas utama sebagai induk— main. Sebab ThreadGroup(ThreadGroup parent, String name), orang tua adalah kelompok yang menjadi parentrujukan. Kode berikut menunjukkan cara menggunakan konstruktor ini untuk membuat sepasang grup untaian:

public static void main (String [] args) { ThreadGroup tg1 = new ThreadGroup ("A"); ThreadGroup tg2 = new ThreadGroup (tg1, "B"); }

Pada kode di atas, utas utama membuat dua grup utas: Adan B. Pertama, utas utama membuat Adengan memanggil ThreadGroup(String name). The tg1orangtua benang kelompok -referenced adalah mainkarena mainmerupakan kelompok benang benang utama. Kedua, utas utama membuat Bdengan memanggil ThreadGroup(ThreadGroup parent, String name). The tg2orangtua benang kelompok -referenced adalah Akarena tg1referensi 's melewati sebagai argumen untuk ThreadGroup (tg1, "B")dan Arekan dengan tg1.

Tip: Setelah Anda tidak perlu lagi hirarki ThreadGroupobjek, panggilan ThreadGroup's void destroy()metode melalui referensi ke ThreadGroupobjek di bagian atas hirarki itu. Jika ThreadGroupobjek teratas dan semua objek subkelompok kekurangan objek thread, destroy()persiapkan objek grup thread tersebut untuk pengumpulan sampah. Jika tidak, destroy()lempar IllegalThreadStateExceptionbenda. Namun, hingga Anda membatalkan referensi ke ThreadGroupobjek teratas (dengan asumsi variabel bidang berisi referensi tersebut), pengumpul sampah tidak dapat mengumpulkan objek tersebut. Referensi objek atas, Anda dapat menentukan apakah panggilan sebelumnya dibuat dengan destroy()metode dengan memanggil ThreadGroup's boolean isDestroyed()metode. Metode itu mengembalikan nilai true jika hierarki grup untaian dihancurkan.

Dengan sendirinya, grup utas tidak berguna. Agar bisa berguna, mereka harus mengelompokkan utas. Anda mengelompokkan utas menjadi grup utas dengan meneruskan ThreadGroupreferensi ke Threadkonstruktor yang sesuai :

ThreadGroup tg = new ThreadGroup ("subgroup 2"); Thread t = new Thread (tg, "my thread");

Kode di atas terlebih dahulu membuat subgroup 2grup dengan mainsebagai grup induk. (Saya berasumsi utas utama mengeksekusi kode.) Kode selanjutnya membuat my threadThreadobjek dalam subgroup 2grup.

Sekarang, mari buat aplikasi yang menghasilkan struktur grup thread hierarki gambar kita:

Kode 1. ThreadGroupDemo.java

// ThreadGroupDemo.java class ThreadGroupDemo { public static void main (String [] args) { ThreadGroup tg = new ThreadGroup ("subgroup 1"); Thread t1 = new Thread (tg, "thread 1"); Thread t2 = new Thread (tg, "thread 2"); Thread t3 = new Thread (tg, "thread 3"); tg = new ThreadGroup ("subgroup 2"); Thread t4 = new Thread (tg, "my thread"); tg = Thread.currentThread ().getThreadGroup (); int agc = tg.activeGroupCount (); System.out.println ("Active thread groups in " + tg.getName () + " thread group: " + agc); tg.list (); } }

ThreadGroupDemomembuat grup utas dan objek utas yang sesuai untuk mencerminkan apa yang Anda lihat pada gambar di atas. Untuk membuktikan bahwa grup subgroup 1dan subgroup 2adalah mainsatu-satunya subgrup, ThreadGroupDemolakukan hal berikut:

  1. Memperoleh kembali referensi ke thread utama ThreadGroupobjek dengan memanggil Thread'statis s currentThread()metode (yang mengembalikan referensi ke thread utama Threadobjek) diikuti oleh Thread' s ThreadGroup getThreadGroup()metode.
  2. Panggilan ThreadGroup's int activeGroupCount()metode pada hanya-kembali ThreadGroupreferensi untuk kembali perkiraan kelompok yang aktif dalam kelompok benang benang utama.
  3. Panggilan ThreadGroup's String getName ()metode untuk kembali nama grup benang benang utama.
  4. Panggilan ThreadGroup's void list ()metode untuk mencetak pada standar rincian perangkat output pada kelompok benang benang utama dan semua subkelompok.

Saat dijalankan, ThreadGroupDemomenampilkan output berikut:

Active thread groups in main thread group: 2 java.lang.ThreadGroup[name=main,maxpri=10] Thread[main,5,main] Thread[Thread-0,5,main] java.lang.ThreadGroup[name=subgroup 1,maxpri=10] Thread[thread 1,5,subgroup 1] Thread[thread 2,5,subgroup 1] Thread[thread 3,5,subgroup 1] java.lang.ThreadGroup[name=subgroup 2,maxpri=10] Thread[my thread,5,subgroup 2]

Output yang dimulai dengan Threadhasil dari list()'panggilan s internal untuk Thread' s toString()metode, format output yang saya jelaskan di Bagian 1. Seiring dengan output yang, Anda melihat output dimulai dengan java.lang.ThreadGroup. Keluaran tersebut mengidentifikasi nama grup utas diikuti dengan prioritas maksimumnya.

Prioritas dan grup utas

Prioritas maksimum grup utas adalah prioritas tertinggi yang dapat dicapai utas mana pun. Pertimbangkan program server jaringan yang disebutkan di atas. Dalam program itu, utas menunggu dan menerima permintaan dari program klien. Sebelum melakukan itu, utas tunggu / terima-permintaan mungkin terlebih dahulu membuat grup utas dengan prioritas maksimum tepat di bawah prioritas utas itu. Nanti, ketika sebuah permintaan tiba, utas tunggu-untuk / terima-permintaan membuat utas baru untuk menanggapi permintaan klien dan menambahkan utas baru ke grup utas yang dibuat sebelumnya. Prioritas utas baru secara otomatis diturunkan ke maksimum grup utas. Dengan begitu, thread wait-for / accept-request merespons permintaan lebih sering karena lebih sering dijalankan.

Java memberikan prioritas maksimum untuk setiap grup utas. Saat Anda membuat grup, Java mendapatkan prioritas tersebut dari grup induknya. Gunakan ThreadGroup's void setMaxPriority(int priority)metode untuk selanjutnya mengatur prioritas maksimal. Setiap utas yang Anda tambahkan ke grup setelah mengatur prioritas maksimumnya tidak dapat memiliki prioritas yang melebihi maksimum. Setiap utas dengan prioritas lebih tinggi secara otomatis turun ketika bergabung dengan grup utas. Namun, jika Anda menggunakan setMaxPriority(int priority)untuk menurunkan prioritas maksimum grup, semua utas yang ditambahkan ke grup sebelum panggilan metode tersebut tetap mempertahankan prioritas aslinya. Misalnya, jika Anda menambahkan utas prioritas 8 ke grup prioritas maksimum 9, dan kemudian menurunkan prioritas maksimum grup itu ke 7, utas prioritas 8 tetap berada di prioritas 8. Setiap saat, Anda dapat menentukan grup utas 's prioritas maksimum dengan memanggil ThreadGroup'sint getMaxPriority()metode. Untuk mendemonstrasikan prioritas dan grup utas, saya menulis MaxPriorityDemo:

Daftar 2. MaxPriorityDemo.java

// MaxPriorityDemo.java class MaxPriorityDemo { public static void main (String [] args) { ThreadGroup tg = new ThreadGroup ("A"); System.out.println ("tg maximum priority = " + tg.getMaxPriority ()); Thread t1 = new Thread (tg, "X"); System.out.println ("t1 priority = " + t1.getPriority ()); t1.setPriority (Thread.NORM_PRIORITY + 1); System.out.println ("t1 priority after setPriority() = " + t1.getPriority ()); tg.setMaxPriority (Thread.NORM_PRIORITY - 1); System.out.println ("tg maximum priority after setMaxPriority() = " + tg.getMaxPriority ()); System.out.println ("t1 priority after setMaxPriority() = " + t1.getPriority ()); Thread t2 = new Thread (tg, "Y"); System.out.println ("t2 priority = " + t2.getPriority ()); t2.setPriority (Thread.NORM_PRIORITY); System.out.println ("t2 priority after setPriority() = " + t2.getPriority ()); } }

Saat dijalankan, MaxPriorityDemomenghasilkan output berikut:

tg maximum priority = 10 t1 priority = 5 t1 priority after setPriority() = 6 tg maximum priority after setMaxPriority() = 4 t1 priority after setMaxPriority() = 6 t2 priority = 4 t2 priority after setPriority() = 4

Grup untaian A(yang tgmereferensikan) dimulai dengan prioritas tertinggi (10) sebagai maksimumnya. Thread X, yang Threadobjeknya t1mereferensikan, bergabung dengan grup dan menerima 5 sebagai prioritasnya. Kami mengubah prioritas utas menjadi 6, yang berhasil karena 6 kurang dari 10. Selanjutnya, kami memanggil setMaxPriority(int priority)untuk mengurangi prioritas maksimum grup menjadi 4. Meskipun utas Xtetap pada prioritas 6, Yutas yang baru ditambahkan menerima 4 sebagai prioritasnya. Akhirnya, upaya untuk meningkatkan Yprioritas utas ke 5 gagal, karena 5 lebih besar dari 4.

Catatan:setMaxPriority(int priority) secara otomatis menyesuaikan prioritas maksimum subgrup grup topik.

Selain menggunakan grup utas untuk membatasi prioritas utas, Anda bisa menyelesaikan tugas lain dengan memanggil berbagai ThreadGroupmetode yang berlaku untuk tiap utas grup. Metode meliputi void suspend(), void resume(), void stop(), dan void interrupt(). Karena Sun Microsystems telah menghentikan tiga metode pertama (mereka tidak aman), kami hanya memeriksanya interrupt().

Interupsi grup utas

ThreadGroup's interrupt()metode memungkinkan thread untuk mengganggu benang dan subkelompok kelompok benang khusus ini. Teknik ini akan terbukti sesuai dalam skenario berikut: Rangkaian utama aplikasi Anda membuat beberapa utas yang masing-masing menjalankan satu unit kerja. Karena semua utas harus menyelesaikan unit kerjanya masing-masing sebelum utas apa pun dapat memeriksa hasilnya, setiap utas menunggu setelah menyelesaikan unit kerjanya. Utas utama memantau status kerja. Setelah semua utas lainnya menunggu, utas utama akan memanggil interrupt()untuk mengganggu waktu tunggu utas lainnya. Kemudian utas tersebut dapat memeriksa dan memproses hasilnya. Kode 3 menunjukkan gangguan grup utas:

Kode 3. InterruptThreadGroup.java

// InterruptThreadGroup.java class InterruptThreadGroup { public static void main (String [] args) { MyThread mt = new MyThread (); mt.setName ("A"); mt.start (); mt = new MyThread (); mt.setName ("B"); mt.start (); try { Thread.sleep (2000); // Wait 2 seconds } catch (InterruptedException e) { } // Interrupt all methods in the same thread group as the main // thread Thread.currentThread ().getThreadGroup ().interrupt (); } } class MyThread extends Thread { public void run () { synchronized ("A") { System.out.println (getName () + " about to wait."); try { "A".wait (); } catch (InterruptedException e) { System.out.println (getName () + " interrupted."); } System.out.println (getName () + " terminating."); } } }