Analisis Kode Sumber Countdownlatch - Countdown ()
Artikel sebelumnya berbicara tentang prinsip AWAIT () di Countdownlatch dari tingkat kode sumber. Artikel ini berbicara tentang Countdown ().
public void Countdown () {// Countdownlatch Sync.Releaseshared (1);} ↓ Public final boolean rilis (int arg) {// aqs if (tryreleaseshared (arg)) {doreleaseshared (); Kembali Benar; } return false;} ↓ boolean yang dilindungi tryreleaseshared (rilis int) {//countdownlatch.sync // jumlah penurunan; sinyal saat transisi ke nol untuk (;;) {int c = getState (); if (c == 0) mengembalikan false; int nextc = c-1; if (compareEndsetState (c, nextc)) mengembalikan nextc == 0; }}Melalui konstruktor CountdownLatch end = Countdownlatch baru (2); status diatur ke 2, jadi c == 2, nextc = 2-1,
Kemudian atur status ke 1 melalui operasi CAS berikut.
Final Boolean CompareEndsetState (int hare, int pembaruan) {// Lihat di bawah untuk pengaturan intrinsik untuk mendukung pengembalian ini tidak aman. }Pada saat ini, NextC bukan 0, dan kembali salah. Tunggu sampai metode hitung mundur () dipanggil dua kali, status == 0, nextc == 0, dan kembali true saat ini.
Masukkan metode doreleaseshared ().
doreleaseshared (); ↓ Private void doreleaseshared () { / * * Pastikan bahwa rilis merambat, bahkan jika ada perolehan /rilis yang sedang diprogram lainnya. Ini dihasilkan dengan cara biasa * mencoba untuk tidak terbalik dari kepala jika membutuhkan sinyal *. Tetapi jika tidak, status diatur untuk menyebarkan untuk * memastikan bahwa pada saat rilis, perambatan berlanjut. * Selain itu, kita harus mengulang jika simpul baru ditambahkan * saat kita melakukan ini. Juga, tidak seperti penggunaan * unparkuccessor lainnya, kita perlu tahu apakah CAS untuk mereset status * gagal, jika demikian perenungan kembali. */ for (;;) {node h = head; if (h! = null && h! = tail) {int ws = h.waitstatus; if (ws == node.signal) {if (! compareEndsetwaitstatus (h, node.signal, 0)) lanjutkan; // Loop untuk memeriksa kembali kasus -kasus Unparkukccessor (H); } else if (ws == 0 &&! compareEndsetwaitstatus (h, 0, node.propagate)) lanjutkan; // loop pada cas gagal} if (h == head) // loop jika head berubah istirahat; }}Mengingat model antrian menunggu saat ini.
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Pada saat ini, kepala bukan nol atau ekor. waitstatus == node.signal, jadi masukkan penilaian jika (! compareEndsetwaitstatus (h, node.signal, 0)).
if (! compareAndsetwaitstatus (h, node.signal, 0)) ↓ /*** CAS Waitstatus bidang sebuah node. */private static boolean final compareEndsetwaitstatus (node node, int happe, int update) {return unsafe.coeandswapint (node, waitstatusoffset, harapkan, perbarui);}Operasi CAS ini menetapkan status ke 0, yang berarti bahwa pelayan di kepala adalah 0 saat ini. Model antrian adalah sebagai berikut
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Metode ini mengembalikan true. Masukkan Unparkuccessor (H);
Unparkuccessor (H); ↓ Private void Unparkuccessor (simpul simpul) { / * * Jika status negatif (yaitu, kemungkinan sinyal yang membutuhkan) Coba * untuk menghapus antisipasi pensinyalan. Tidak apa -apa jika ini * gagal atau jika status diubah dengan utas menunggu. */ int ws = node.waitstatus; if (ws <0) compareEndsetwaitstatus (node, ws, 0); / * * Thread to Unpark diadakan di penerus, yang biasanya * hanya simpul berikutnya. Tetapi jika dibatalkan atau tampaknya nol, * melintasi ke belakang dari ekor untuk menemukan penerus * yang tidak dibatalkan. */ Node s = node.next; if (s == null || s.waitstatus> 0) {s = null; untuk (node t = tail; t! = null && t! = node; t = t.prev) if (t.waitstatus <= 0) s = t; } if (s! = null) locksupport.unpark (s.thread);}S adalah simpul kepala kepala, yaitu simpul dengan utas saat ini. s! = null, dan s.waitstatus == 0, jadi masukkan locksupport.unpark (s.thread);
public static void unpark (thread thread) {if (thread! = null) unsafe.unpark (thread); }Artinya, utas yang membuka kunci diblokir. Wasit diizinkan untuk meniup peluit!
Prinsip Countdown () sangat jelas.
Setiap kali metode hitung mundur () dieksekusi, status dikurangi dengan 1. Sampai status == 0, utas yang diblokir dalam antrian mulai dirilis, dan utas pada node berikutnya dilepaskan sesuai dengan keadaan Waitstatus di node pendahulunya.
Oke, kembali ke pertanyaan artikel sebelumnya, kapan loop berikut akan keluar (loop dalam metode menunggu)
untuk (;;) {node final p = node.predecessor (); if (p == head) {int r = tryacquireshared (arg); if (r> = 0) {setheadandpropagate (node, r); p.next = null; // bantu GC gagal = false; kembali; }} if (harus diparkirfailedacquire (p, node) && parkandcheckinterrupt ()) lempar interruptedException baru ();}Pada saat ini, status == 0, jadi masukkan metode setheadandpropagate.
setheadandpropagate (node, r); ↓ private void setheadandpropagate (simpul simpul, int propagate) {node h = head; // Rekam kepala lama untuk pemeriksaan di bawah sethead (node); / * * Cobalah untuk memberi sinyal simpul antrian berikutnya jika: * Perbanyakan ditunjukkan oleh penelepon, * atau direkam (sebagai h.waitstatus baik sebelum * atau setelah sethead) dengan operasi sebelumnya * (Catatan: ini menggunakan tanda-tanda waitstatus karena * mungkin tidak ada yang diketahui, karena ia tidak tahu, karena ia tidak tahu. Bangun yang tidak perlu, tetapi hanya ketika ada beberapa * balap memperoleh/rilis, jadi sebagian besar membutuhkan sinyal sekarang atau segera * bagaimanapun. */ if (propagate> 0 || h == null || h.waitstatus <0 || (h = head) == null || h.waitstatus <0) {node s = node.next; if (s == null || s.isshared ()) doreleaseshared (); }} ↓ Private void setHead (simpul simpul) {head = node; node.thread = null; node.prev = null;}Metode ini mengubah simpul penerus kepala menjadi kepala. Setelah metode ini, simpul simpul berikutnya diatur ke nol, dan model menjadi gambar berikut
Sebelumnya +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Artinya, ekor kepala simpul dan hal -hal lain diatur ke nol, menunggu GC mendaur ulang. Pada saat ini, kembali, melompat keluar dari loop untuk, dan antrian dibersihkan.
Berikut adalah demonstrasi dari seluruh proses
setheadandpropagate (node, r); +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Thread = null | <---- node (ekor) | CurrentThread | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- node (ekor) | CurrentThread | + -------------------------------+ ↓ +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Inti dari CountdownLatch adalah antrian utas pemblokiran, yang merupakan antrian yang dibangun dari daftar tertaut, yang berisi utas dan waitstatus, di mana Waitstatus menggambarkan status utas simpul pengganti.
Negara adalah bendera yang sangat penting. Saat membangun, diatur ke nilai N yang sesuai. Jika n! = 0, antrian pemblokiran akan diblokir sepanjang waktu kecuali utas terganggu.
Setiap kali metode hitung mundur () dipanggil, status-1 digunakan, dan metode waait () digunakan untuk menambahkan utas yang memanggil metode ke antrian pemblokiran sampai status == 0, dan utas tidak dapat dilepaskan.
Di atas adalah semua konten artikel ini. Saya berharap ini akan membantu untuk pembelajaran semua orang dan saya harap semua orang akan lebih mendukung wulin.com.