Melalui analisis dalam artikel sebelumnya, kita tahu bahwa ada tiga cara untuk mendapatkan kunci dengan mode eksklusif, yaitu, untuk mendapatkan tanpa interupsi utas respons, untuk mendapatkan interupsi utas respons, dan untuk mendapatkan waktu waktu. Ada juga tiga cara untuk memperoleh kunci dalam mode bersama, dan pada dasarnya sama. Jika kita mencari tahu satu arah, kita dapat dengan cepat memahami cara lain. Meskipun Kode Sumber AbstractQueuedSynchronizer memiliki lebih dari seribu baris, itu juga diulang berkali -kali, sehingga pembaca tidak boleh takut pada awalnya. Baca saja dengan sabar dan perlahan, Anda secara alami akan secara bertahap memahaminya. Dalam pengalaman pribadi saya, ada beberapa aspek yang lebih penting untuk dipahami ketika membaca kode sumber AbstractQueuedSynchronizer, yaitu perbedaan antara mode eksklusif dan mode bersama, keadaan node yang menunggu, dan pemahaman antrian bersyarat. Jika Anda memahami poin -poin penting ini, maka pembacaan kode sumber berikutnya akan jauh lebih mudah. Tentu saja, ini diperkenalkan dalam artikel saya "Java Concurrency Series [1] ---- Analisis Kode Sumber Sumber AbstractQueuedSynchronizer", dan pembaca dapat memeriksanya terlebih dahulu. Artikel ini menganalisis mode berbagi menjadi tiga cara untuk memperoleh kunci dan satu cara untuk melepaskan kunci.
1. Tidak menanggapi akuisisi interupsi utas
// memperoleh kunci dalam mode non-interterrupt (mode bersama) public void Acquireshared (int arg) {// 1. Cobalah untuk mendapatkan kunci jika (TRYACQUIRESHARED (arg) <0) {// 2. Jika akuisisi gagal, masukkan metode ini doacquireshared (arg); }} // Cobalah untuk memperoleh kunci (mode bersama) // angka negatif: menunjukkan bahwa akuisisi gagal // Nilai nol: menunjukkan bahwa simpul saat ini berhasil diperoleh, tetapi simpul penerus tidak dapat lagi mendapatkan // angka positif: menunjukkan bahwa node yang berhasil (node yang tidak dapat diperoleh (node yang tidak berhasil juga dapat diperoleh dengan sukses (node yang tidak berhasil, dan node yang tidak berhasil, dan node yang tidak berhasil, dan node yang tidak berhasil,Memanggil metode akuireshared adalah cara untuk memperoleh kunci tanpa menanggapi interupsi utas. Dalam metode ini, TREACQUIRESHARED pertama kali dipanggil untuk mencoba memperoleh kunci. Metode Tryacquireshared mengembalikan keadaan memperoleh kunci. Di sini AQS menetapkan bahwa jika status pengembalian negatif, itu berarti bahwa simpul saat ini gagal mendapatkan kunci. Jika 0 berarti bahwa simpul saat ini memperoleh kunci, tetapi simpul berikutnya tidak dapat diperoleh lagi. Jika positif, itu berarti bahwa node saat ini memperoleh kunci, dan node selanjutnya dari kunci ini juga dapat diperoleh dengan sukses. Ketika subclass mengimplementasikan logika untuk mendapatkan kunci dengan metode tryacquireshared, nilai pengembalian perlu mematuhi konvensi ini. Jika nilai pengembalian panggilan tryacquireshared kurang dari 0, itu berarti bahwa upaya untuk memperoleh kunci gagal. Selanjutnya, hubungi metode doacquireshared untuk menambahkan utas saat ini ke antrian sinkronisasi. Kami melihat metode doacquireshared.
// dapatkan (mode bersama) dalam antrian sinkronisasi private void doacquireshared (int arg) {// Tambahkan ke simpul final antrian sinkronisasi node = addWaiter (node.shared); boolean gagal = true; coba {boolean interrupted = false; untuk (;;) {// Dapatkan simpul ke depan dari simpul Node saat ini p = node.predecessor (); // Jika simpul ke depan adalah simpul kepala, cobalah untuk mendapatkan kunci lagi jika (p == head) {// Cobalah untuk memperoleh kunci lagi dan mengembalikan status akuisisi // r <0, menunjukkan bahwa akuisisi gagal // r = 0, yang menunjukkan bahwa node saat ini berhasil, tetapi node berikutnya tidak dapat diperoleh NODE NODE IS NODE NODE IS ADALAH BAGAIMANA BAGAIMAN. berhasil mendapatkan int r = tryacquireshared (arg); if (r> = 0) {// Untuk tujuan ini, ini menunjukkan bahwa simpul saat ini telah berhasil memperoleh kunci. Pada saat ini, itu akan menyebarkan informasi status kunci ke simpul setheadandpropagate node berikutnya (node, r); p.next = null; // Jika permintaan interupsi diterima selama pemblokiran utas, tanggapi permintaan pada langkah ini jika (terganggu) {selfinterrupt (); } gagal = false; kembali; }} // Setiap kali akuisisi kunci gagal, itu akan menentukan apakah utas dapat ditangguhkan. Jika memungkinkan, utas akan ditangguhkan dalam metode ParkandCheckInterrupt jika (harusKarkAfterFailedAcquire (p, node) && parkandcheckinterrupt ()) {interrupted = true; }}} akhirnya {if (gagal) {cancelacquire (node); }}}Memasuki metode doacquireshared terlebih dahulu, panggil metode Addwaaiter untuk membungkus utas saat ini ke dalam sebuah node dan masukkan di ujung antrian sinkronisasi. Kami telah berbicara tentang proses menambahkan node ketika berbicara tentang mode eksklusif, jadi saya tidak akan membicarakannya di sini. Setelah sebuah node memasuki antrian sinkronisasi, jika menemukan bahwa simpul di depannya adalah simpul kepala, karena utas node kepala telah memperoleh kunci dan memasuki ruangan, maka gilirannya untuk memperoleh kunci. Oleh karena itu, simpul saat ini tidak akan menggantung dirinya terlebih dahulu, tetapi akan mencoba untuk mendapatkan kunci lagi. Jika orang di depan hanya melepaskan kunci dan pergi, simpul saat ini dapat berhasil mendapatkan kunci. Jika orang di depan belum merilis kunci, itu akan menghubungi metode yang harus diterjemahkan. Dalam metode ini, keadaan simpul kepala akan diubah menjadi sinyal. Hanya dengan memastikan bahwa keadaan simpul sebelumnya adalah sinyal, simpul saat ini dapat menggantung dirinya dengan percaya diri. Semua utas akan ditangguhkan dalam metode ParkandCheckInterrupt. Jika node saat ini berhasil memperoleh kunci, maka metode SetHeadAndPropagate akan dipanggil untuk mengatur dirinya sendiri sebagai simpul kepala dan membangunkan node yang juga merupakan mode bersama di belakang. Mari kita lihat operasi spesifik dari metode SetHeadAndPropagate.
// Atur node head dan persiapkan keadaan kunci (mode bersama) void pribadi setHeadandPropagate (simpul simpul, int propagate) {node h = head; // atur node yang diberikan sebagai head node sethead (node); // jika menyebar lebih besar dari 0, itu berarti bahwa kunci dapat memperoleh IF (Propagate> 0 || h == null || h.waitstatus <0) {// Dapatkan simpul penerus simpul simpul yang diberikan s = node.next; // Jika simpul penerus dari simpul yang diberikan kosong, atau keadaannya adalah keadaan bersama jika (s == null || s.isshared ()) {// bangun simpul penerus node doreleaseshared (); }}} // Rilis Operasi Kunci (Mode Bersama) Private Void DoreleAseShared () {for (;;) {// Dapatkan simpul kepala dari simpul antrian sinkron h = head; if (h! = null && h! = tail) {// Dapatkan status menunggu simpul kepala int ws = h.waitstatus; // Jika status simpul kepala adalah sinyal, itu berarti bahwa seseorang mengantri di belakang IF (ws == node.signal) {// Dapatkan keadaan menunggu simpul kepala ke 0 if (! CompareEndsetwaitstatus (h, node.signal, 0)) {lanjutkan; } // Bangun simpul pengganti Unparkuccessor (H); // Jika status simpul kepala adalah 0, itu berarti bahwa tidak ada yang mengantri nanti, cukup ubah status kepala menjadi propagasi} lain jika (ws == 0 &&! CompareEndsetwaitStatus (h, 0, node.propagate)) {lanjutkan; }} // Hanya dengan memastikan bahwa simpul kepala belum dimodifikasi selama periode tersebut, Anda dapat keluar dari loop jika (h == head) {break; }}}Memanggil metode SetHeadAndPropagate pertama kali menetapkan dirinya sebagai simpul kepala, dan kemudian memutuskan apakah akan membangunkan simpul penerus berdasarkan nilai pengembalian metode tryacquireshared yang dilewati. Seperti yang disebutkan sebelumnya, ketika nilai pengembalian lebih besar dari 0, itu berarti bahwa simpul saat ini telah berhasil memperoleh kunci, dan simpul selanjutnya juga dapat berhasil memperoleh kunci. Pada saat ini, simpul saat ini perlu membangunkan simpul yang juga dalam mode bersama. Perhatikan bahwa setiap kali Anda bangun, itu hanya untuk membangunkan simpul berikutnya. Jika simpul terakhir tidak dalam mode bersama, simpul saat ini akan langsung memasuki ruangan dan tidak akan membangunkan simpul lebih lanjut. Pengoperasian Node Penerus Bangun dalam Mode Bersama dilakukan dalam metode Doreleaseshared. Operasi bangun dari mode bersama dan mode eksklusif pada dasarnya sama. Keduanya menemukan merek di kursi Anda (status menunggu). Jika merek itu sinyal, itu berarti seseorang perlu membantu membangunkannya nanti. Jika mereknya 0, itu berarti tidak ada yang mengantri dalam antrian saat ini. Dalam mode eksklusif, jika Anda menemukan bahwa tidak ada yang mengantri, Anda akan meninggalkan antrian secara langsung. Dalam mode bersama, jika Anda menemukan bahwa tidak ada yang mengantri di belakang antrian, simpul saat ini masih akan meninggalkan catatan kecil sebelum pergi (atur status tunggu untuk menyebarkan) untuk memberi tahu orang -orang kemudian keadaan kunci yang tersedia. Kemudian ketika orang yang datang kemudian dapat menilai apakah akan secara langsung memperoleh kunci berdasarkan negara bagian ini.
2. Respons terhadap akuisisi interupsi utas
// Mengakuisisi kunci dalam mode interruptible (mode bersama) public final void AcquireSharedIbrnya (int arg) melempar InterruptedException {// Tentukan apakah utas tersebut terputus, jika demikian, lempar pengecualian jika (thread.echerrupted ()) {lempar baru interruptEcception (); } // 1. Cobalah untuk mendapatkan kunci jika (TRYACQUIRESHARED (arg) <0) {// 2. Jika akuisisi gagal, masukkan metode ini doAcquiresharedIbrnya (arg); }} // mengakuisisi dalam mode interruptible (mode bersama) membatalkan doAcquiresharedIbrnya (int arg) melempar interruptedException {// masukkan simpul saat ini ke ekor dari simpul antrian sinkronisasi node final node = addwaaiter (node.shared); boolean gagal = true; coba {untuk (;;) {// dapatkan simpul node sebelumnya p = node.predecessor (); if (p == head) {int r = tryacquireshared (arg); if (r> = 0) {setheadandpropagate (node, r); p.next = null; gagal = false; kembali; }} if (seharusnyaParkAfterFailedAcquire (p, node) && parkandCheckInterrupt ()) {// Jika utas menerima permintaan interupsi selama proses pemblokiran, itu akan segera melempar pengecualian di sini melempar interruptException baru (); }}} akhirnya {if (gagal) {cancelacquire (node); }}}Cara memperoleh kunci dalam menanggapi interupsi utas dan cara memperoleh kunci sebagai respons terhadap interupsi utas pada dasarnya sama dalam prosesnya. Satu -satunya perbedaan adalah di mana merespons permintaan interupsi utas. Ketika interupsi utas tidak menanggapi interupsi utas untuk mendapatkan kunci, utas dibangunkan dari metode ParkandcheckInterrupt. Setelah bangun, segera kembali apakah permintaan interupsi telah diterima. Bahkan jika permintaan interupsi diterima, itu akan terus berputar sampai diperoleh sampai menanggapi permintaan interupsi dan menggantung sendiri. Utas akan segera menanggapi permintaan interupsi setelah utas dibangunkan. Jika interupsi utas diterima selama proses pemblokiran, interupsi Exception akan segera dilemparkan.
3. Tetapkan waktu batas waktu untuk mendapatkan
// Mengakuisisi kunci dengan batas waktu terbatas (mode bersama) Publik Boolean TryAcquiresharedNanos (int arg, Long NanosTimeout) melempar InterruptedException {if (thread.interrupted ()) {Throw New InterruptException (); } // 1. Memanggil tryacquireshared untuk mencoba memperoleh kunci // 2. Jika akuisisi gagal, hubungi doacquiresharednanos return tryacquireshared (arg)> = 0 || doacquiresharednanos (arg, nanostimeout);} // mengakuisisi kunci dengan batas waktu terbatas (mode bersama) boolean doacquiresharednanos (int arg, nanostimmeout panjang) melempar interruptException {long lasttime = system.nanoTime (); Node Node Akhir = AddWaiter (Node.Shared); boolean gagal = true; coba {for (;;) {// dapatkan node sebelumnya dari node node saat ini node p = node.predecessor (); if (p == head) {int r = tryacquireshared (arg); if (r> = 0) {setheadandpropagate (node, r); p.next = null; gagal = false; Kembali Benar; }} // Jika batas waktu digunakan, akuisisi akan diakhiri dan informasi kegagalan akan dikembalikan jika (nanoSTimeout <= 0) {return false; } // 1. Periksa apakah persyaratan suspensi utas dipenuhi (dijamin status simpul maju adalah sinyal) // 2. Periksa apakah waktu batas waktu lebih besar dari waktu putaran jika (harus diparkirfailedacquire (p, node) && nanoSTimeout> spinforTimeOutThreshold) {// Jika dua kondisi di atas dipenuhi, utas saat ini akan ditangguhkan untuk periode waktu locksupport.parknanos (ini, nanoStieout); } long now = system.nanoTime (); // Waktu Batas Waktu Setiap Waktu Mengurangi Waktu NanoSTimeOut Akuisisi Kunci - = Sekarang - Terakhir Waktu; terakhir kali = sekarang; // Jika permintaan interupsi diterima selama pemblokiran, pengecualian akan segera dilemparkan jika (thread.interrupted ()) {lempar New InterruptedException (); }}} akhirnya {if (gagal) {cancelacquire (node); }}}Jika Anda memahami dua metode akuisisi di atas, akan sangat mudah untuk mengatur metode akuisisi waktu batas waktu. Proses dasarnya sama, terutama untuk memahami mekanisme batas waktu. Jika kunci diperoleh untuk pertama kalinya, metode DoacquiresharedNanos akan dipanggil dan waktu batas waktu akan diteruskan. Setelah memasukkan metode, kunci akan diperoleh lagi sesuai dengan situasinya. Jika kunci gagal lagi, utas harus dianggap ditangguhkan. Pada saat ini, kami akan menentukan apakah waktu batas waktu lebih besar dari waktu putaran. Jika demikian, utas akan ditangguhkan untuk jangka waktu tertentu. Kalau tidak, kami akan terus mencoba mendapatkannya. Setelah setiap kali kami memperoleh kunci, kami akan mengurangi waktu kunci untuk mendapatkannya. Kami akan mengulang seperti ini sampai waktu batas waktu habis. Jika kunci belum diperoleh, akuisisi akan diakhiri dan bendera kegagalan akuisisi akan dikembalikan. Utas merespons interupsi utas sepanjang periode.
4. Operasi Dequeuing Node Dalam Mode Bersama
// Pengoperasian Releasing Lock (Mode Bersama) Public Final Boolean Releaseshared (int arg) {//1.try untuk melepaskan kunci IF (tryreleaseshared (arg)) {// 2. Jika rilis berhasil, bangun utas lain Doreleaseshared (); Kembali Benar; } return false;} // Coba lepaskan kunci (mode bersama) boolean tryreleaseshared (int arg) {lempar baru unsupportedoperationException ();} // operasi pelepasan kunci (mode bersama) void private doreleaseshared () {for (;;) {// Dapatkan node head dari synchon synchon () {for (;; if (h! = null && h! = tail) {// Dapatkan status menunggu simpul kepala int ws = h.waitstatus; // Jika status simpul kepala adalah sinyal, itu berarti seseorang mengantri nanti jika (ws == node.signal) {// Pertama perbarui keadaan menunggu node kepala ke 0 if (! CompareEndsetwaitstatus (h, node.signal, 0)) {lanjutan; } // Bangun node berikutnya Unparkuccessor (H); // Jika status simpul kepala adalah 0, itu berarti bahwa tidak ada yang mengantri nanti, itu hanya mengubah keadaan kepala menjadi propagasi} lain jika (ws == 0 &&! CompareEndsetwaitstatus (h, 0, node.propagate)) {lanjutkan; }} // Loop hanya dapat dipecahkan IF (h == head) {break; }}}Setelah utas selesai bekerja di dalam ruangan, ia akan memanggil metode Releaseshared untuk melepaskan kunci. Pertama, itu akan memanggil metode tryreleaseshared untuk mencoba melepaskan kunci. Logika penilaian dari metode ini diimplementasikan oleh subkelas. Jika rilis berhasil, hubungi metode Doreleaseshared untuk membangunkan simpul penerus. Setelah berjalan keluar dari ruangan, ia akan menemukan kursi asli (kepala simpul) dan melihat apakah ada yang meninggalkan catatan kecil di kursi (sinyal status). Jika demikian, bangun simpul penerus. Jika tidak ada (status 0) berarti bahwa tidak ada yang mengantri dalam antrian, maka hal terakhir yang harus dilakukan sebelum pergi adalah pergi, yaitu meninggalkan catatan kecil di kursinya (status diatur untuk menyebarkan) untuk memberi tahu orang -orang di belakang kunci untuk memperoleh negara. Satu -satunya perbedaan antara seluruh proses pelepasan kunci dan mode eksklusif adalah beroperasi pada langkah terakhir ini.
Catatan: Semua analisis di atas didasarkan pada JDK1.7, dan akan ada perbedaan antara versi yang berbeda, pembaca perlu memperhatikan.
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.