Analisis Kode Sumber Countdownlatch - Await (), konten spesifik adalah sebagai berikut
Artikel sebelumnya berbicara tentang cara menggunakan CountdownLatch. Artikel ini akan berbicara tentang prinsip AWAIT () dari tingkat kode sumber.
Kita sudah tahu bahwa menunggu dapat menjaga utas saat ini dalam keadaan pemblokiran sampai jumlah kait nol (atau gangguan utas).
Di bawah ini adalah kode sumbernya.
end.aWait (); ↓ public void waait () melempar InterruptedException {Sync.AcquiresharedIbrnya (1);}Sinkronisasi adalah kelas batin dari Countdownlatch. Ini definisinya.
Sinkronisasi kelas akhir statis privat meluas abstractqueuedsynchronizer {...}Ini mewarisi abstractqueuedsynchronizer. AbstractQueuedsynchronizer Kelas ini termasuk dalam kelas yang sangat penting di utas Java.
Ini menyediakan kerangka kerja untuk mengimplementasikan kunci pemblokiran dan sinkronisasi terkait (seperti sinyal, acara, dll.) Yang mengandalkan antrian menunggu FIFO.
Terus pergi dan lompat ke kelas abstractqueuedsynchronizer.
Sync.AcquiresharedIbrnya (1); ↓ public final void AcquireSharedInterTried (int arg) // AbstractQueuedSynchronizer melempar InterruptedException {if (thread.interrupted ()) Lempar New InterruptedException (); if (tryacquireshared (arg) <0) doacquiresharedIbrnya (arg);}Ada dua penilaian di sini. Pertama, tentukan apakah utas terganggu, dan kemudian buat penilaian berikutnya. Di sini kita terutama melihat penilaian kedua.
intrected int tryacquireshared (int mengakuisisi) {return (getState () == 0)? 1: -1;}Perlu dicatat bahwa metode tryacquireshared diimplementasikan secara sinkron.
Meskipun ada implementasi di AbstractQueuedSynchronizer, implementasi default adalah untuk melempar pengecualian.
Tryacquireshared metode ini digunakan untuk menanyakan apakah status objek saat ini dapat diizinkan untuk memperoleh kunci.
Kita dapat melihat bahwa secara sinkron, kita mengembalikan nilai int yang sesuai dengan menentukan apakah keadaan adalah 0.
Jadi apa arti negara bagian?
/*** Keadaan sinkronisasi. */ status int private volatile;
Kode di atas dengan jelas menunjukkan bahwa status mewakili status sinkronisasi.
Perlu dicatat bahwa status menggunakan kata kunci yang mudah menguap untuk memodifikasinya.
Kata kunci yang mudah menguap dapat memastikan bahwa modifikasi status segera diperbarui ke memori utama. Ketika utas lain perlu dibaca, nilai baru akan dibaca dalam memori.
Artinya, visibilitas negara dijamin. Ini adalah data terbaru.
Apa keadaan yang datang ke sini?
Di sini kita perlu melihat konstruktor Countdownlatch.
Countdownlatch end = New Countdownlatch (2); ↓ Public CountdownLatch (int count) {if (count <0) Lempar IllegalArgumentException baru ("Count <0"); this.sync = sinkronisasi baru (count);} ↓ sinkronisasi (penghitungan int) {setState (count);}Ternyata angka -angka dalam konstruktor digunakan untuk mengatur keadaan.
Jadi kami memiliki state == 2 di sini. tryacquireshared kembali -1. Masukkan di bawah
doacquiresharedIbrnya (arg); ↓ Private void doAcquiresharedInterricture (int arg) melempar InterruptedException {node final node = addwaaiter (node.shared); boolean gagal = true; coba {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 (harusKarkafterfailedacquire (p, node) && parkandcheckinterrupt ()) lempar interruptException baru (); }} akhirnya {if (gagal) cancelacquire (node); }}OK, kode ini agak panjang, dan beberapa fungsi dipanggil di dalamnya. Mari kita lihat satu per satu.
Node kelas baru muncul di baris pertama.
Node adalah kelas internal di kelas AQS (AbstractQueuedSynchronizer), yang mendefinisikan struktur rantai. Seperti yang ditunjukkan di bawah ini.
+------+prev+-----++-----+head | | <---- | | <---- | | | ekor +----- + +----- + +----- +
Ingat struktur ini.
Ada juga metode di baris pertama dari kode addWaiter (node.shared).
addwaaiter (node.shared) //node.shared berarti bahwa node dalam mode bersama ↓ private node addwaaiter (mode node) {node node = node baru (thread.currentThread (), mode); // coba jalur cepat enq; cadangan ke enq penuh pada simpul kegagalan pred = tail; // ekor simpul volatil transien pribadi; if (pred! = null) {node.prev = pred; if (compareANDSettail (pred, node)) {pred.next = node; return node; }} enq (node); mengembalikan simpul;}Pertama, sebuah simpul dibangun dan utas saat ini disimpan. Mode adalah mode bersama.
Ekor berarti bahwa ujung antrian dari antrian menunggu adalah nol pada saat ini. Jadi pred == null memasuki enq (node);
enq (node) ↓ private node enq (node node akhir) {for (;;) {node t = tail; if (t == null) {// Harus menginisialisasi if (compareEndsethead (new node ())) tail = head; } else {node.prev = t; if (compareANDSettail (t, node)) {t.next = node; mengembalikan t; }}}}Ekor yang sama adalah nol, masukkan CompareEndsethead.
CompareANDSethead (node baru ()) ↓/*** CAS Head Field. Hanya digunakan oleh enq. */Private Final Boolean CompareEndsetHead (pembaruan simpul) {return unsafe.ComeandsWapObject (this, headoffset, null, update);}Ini adalah operasi CAS. Jika kepala nol, kepala antrian menunggu akan diatur ke nilai pembaruan, yang merupakan simpul baru.
ekor = kepala; Maka ekor tidak lagi nol saat ini. Masukkan siklus berikutnya.
Kali ini, titik pertama pointer prev node ke ekor, lalu atur node ke ekor melalui operasi CAS, dan kembalikan ekor antrian, yaitu node.
Model antrian yang menunggu berubah sebagai berikut
+ ------+ Sebelumnya +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Oke, ketika Anda sampai di sini, metode menunggu kembali, itu adalah utas yang sama dengan simpul utas saat ini.
Kembali ke DoAcquiresharedIbrnya (int arg) dan masukkan loop berikut.
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, dengan asumsi bahwa keadaan masih lebih besar dari 0, maka r <0 saat ini, jadi masukkan metode yang harus diterjemahkan.
HaruskahrahaFailEdacquire (p, node) ↓ Private static boolean harus diparkirfailedacquire (node pred, simpul simpul) {int ws = pred.waitstatus; if (ws == node.signal) // sinyal int static final = -1; / * * Node ini telah menetapkan status yang meminta rilis * untuk memberi sinyal, sehingga dapat dengan aman parkir. */ return true; if (ws> 0) { / * * pendahulu dibatalkan. Lewati pendahulunya dan * diindikasikan coba lagi. */ do {node.prev = pred = pred.prev; } while (pred.waitstatus> 0); pred.next = node; } else { / * * waitstatus harus 0 atau diperbanyak. Tunjukkan bahwa kita * membutuhkan sinyal, tetapi jangan parkir. Penelepon perlu * coba lagi untuk memastikan itu tidak dapat memperoleh sebelum parkir. */ compareAndsetwaitstatus (pred, ws, node.signal); } return false;} ↓/*** CAS Waitstatus bidang sebuah node. */private static boolean final compareEndsetwaitstatus (node node, int happe, int update) {return unsafe.coeandswapint (node, waitstatusoffset, harapkan, perbarui);}Anda dapat melihat bahwa harus ParkAfterFailedacquire juga berjalan jauh ke CompareANDSetWaitstatus.
CompareANDSETWAITSTATUS Mengatur Waitstatus of Prev ke Node.Signal.
Node.signal berarti bahwa utas pada node berikutnya harus membongkar (mirip dengan dibangunkan). Metode ini mengembalikan false.
Setelah siklus ini, model antrian menjadi keadaan berikut
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Karena harus ParkAfterFailedAcquire mengembalikan False, kami tidak akan lagi melihat kondisi berikut. Lanjutkan loop di untuk (;;).
Jika negara bagian masih lebih besar dari 0, masukkan lagi untuk harus diparkir.
Kali ini, karena waitstatus di kepala adalah node.signal, harus diparkir darifailedacquire kembali.
Kali ini saya perlu melihat metode ParkandCheckInterrupt.
private boolean parkandcheckinterrupt () {locksupport.park (ini); return thread.interrupted (); }Oke, utasnya tidak terganggu, jadi, kembalikan salah. Lanjutkan loop di untuk (;;).
Jika keadaan selalu lebih besar dari 0 dan utas tidak terganggu, maka selalu ada di loop ini. Artinya, wasit yang disebutkan dalam artikel sebelumnya bahwa mereka selalu enggan mengumumkan akhir dari permainan.
Jadi dalam keadaan apa loop akan pecah? Artinya, dalam keadaan apa menyatakan kurang dari 0? Saya akan menjelaskan artikel berikutnya.
Singkatnya, metode waait () sebenarnya adalah untuk menginisialisasi antrian, menambahkan utas yang perlu ditunggu (status> 0) ke antrian, dan menggunakan waitstatus untuk menandai status utas dari simpul penerus.
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.