Dalam kode program C, kita dapat menggunakan kunci mutex yang disediakan oleh sistem operasi untuk mencapai akses mutex ke blok sinkron dan pemblokiran utas dan pekerjaan bangun. Namun, di Java, selain menyediakan kata kunci Lockapi, yang disinkronkan juga disediakan pada tingkat sintaksis untuk mengimplementasikan primitif sinkronisasi mutex. Jadi, bagaimana Anda mengimplementasikan kunci yang disinkronkan di JVM?
1. Representasi bytecode yang disinkronkan:
Ada dua sintaksis tersinkronisasi bawaan dalam bahasa Java: 1. Pernyataan yang disinkronkan; 2. Metode yang disinkronkan. Untuk pernyataan yang disinkronkan ketika kode sumber Java dikompilasi ke dalam bytecode oleh Javac, Monitorenter dan instruksi bytecode monitorexit akan dimasukkan masing -masing pada posisi masuk dan keluar dari blok sinkronisasi. Metode yang disinkronkan akan diterjemahkan ke dalam metode panggilan dan pengembalian metode biasa seperti: Instruksi Invokevirtual dan Areturn. Tidak ada instruksi khusus di level bytecode VM untuk mengimplementasikan metode yang dimodifikasi yang disinkronkan. Sebagai gantinya, posisi bendera yang disinkronkan 1 dalam bidang Metode Access_Flags dari metode ini ditempatkan di tabel metode file kelas, menunjukkan bahwa metode ini adalah metode yang disinkronkan dan menggunakan objek yang memanggil metode atau kelas milik metode untuk mewakili Klass sebagai objek kunci.
2. Optimalisasi kunci di JVM:
Sederhananya, MonitorEnter dan Monitorexit bytecode dalam JVM mengandalkan mutexlock dari sistem operasi yang mendasarinya untuk mengimplementasikannya. Namun, karena menggunakan mutexlock membutuhkan penangguhan utas saat ini dan beralih dari keadaan pengguna ke status kernel untuk dieksekusi, switching ini sangat mahal; Namun, dalam kebanyakan kasus dalam kenyataan, metode sinkronisasi dijalankan dalam lingkungan utamanya (lingkungan kompetisi tanpa kunci). Jika mutexlock dipanggil setiap kali, itu akan secara serius mempengaruhi kinerja program. Namun, di JDK1.6, banyak optimisasi telah diperkenalkan dengan implementasi kunci, seperti terkunci kunci, eliminasi kunci, penguncian ringan, penguncian bias, pemintalan adaptif dan teknologi lainnya untuk mengurangi overhead operasi kunci.
LockCoarsening: Artinya, mengurangi operasi kunci dan kunci yang tidak perlu, ungkapkan beberapa kunci kontinu ke dalam kunci dengan kisaran yang lebih besar.
Eliminasi kunci: Melalui analisis pelarian oleh runtime JIT Compiler, beberapa perlindungan kunci dihilangkan. Beberapa data yang tidak dibagikan oleh utas lain di luar blok sinkronisasi saat ini. Melalui analisis melarikan diri, ruang objek dapat dialokasikan pada tumpukan lokal Thread (pada saat yang sama, ia juga dapat mengurangi overhead pengumpulan sampah di tumpukan).
Light Weightlocking: Implementasi kunci ini didasarkan pada asumsi bahwa dalam kasus nyata, sebagian besar kode sinkronisasi dalam program kami umumnya dalam keadaan kompetisi bebas kunci (mis., Lingkungan eksekusi tunggal). Dalam hal persaingan bebas kunci, ia dapat sepenuhnya menghindari memanggil mutex kelas berat di tingkat sistem operasi. Sebaliknya, di Monitorenter dan Monitorexit, Anda hanya perlu mengandalkan instruksi atom CAS untuk menyelesaikan akuisisi dan pelepasan kunci. Ketika ada kompetisi kunci, utas yang gagal menjalankan instruksi CAS akan menghubungi sistem operasi Mutex untuk memasuki status pemblokiran dan bangun ketika kunci dilepaskan (langkah pemrosesan spesifik dibahas secara rinci di bawah).
Biasedlocking: Ini untuk menghindari pelaksanaan instruksi atom CAS yang tidak perlu selama akuisisi kunci dalam hal kompetisi bebas kunci, karena meskipun instruksi atom CAS relatif kecil dalam biaya dibandingkan dengan kunci kelas berat, mereka masih memiliki keterlambatan lokal yang sangat besar (lihat artikel ini).
Adaptive Spinning: Ketika sebuah utas gagal melakukan operasi CAS selama akuisisi kunci ringan, itu akan memasuki menunggu sibuk sebelum memasuki sistem operasi kunci kelas berat (mutexsemaphore) yang terkait dengan monitor dan kemudian coba lagi. Jika masih gagal setelah sejumlah upaya tertentu, semaphore yang terkait dengan monitor (mis., Mutex Lock) dipanggil untuk memasuki keadaan pemblokiran.
3. Objectheader:
Saat membuat objek di JVM, dua header objek berukuran kata akan ditambahkan di depan objek. Satu kata pada mesin 32-bit adalah 32bit. Isi yang berbeda disimpan di Markworld sesuai dengan bit status yang berbeda. Seperti yang ditunjukkan pada gambar di atas, dalam kunci yang ringan, Markword dibagi menjadi dua bagian. Pada awalnya, kata kunci diatur ke kode hash, dan tiga bit terendah mewakili keadaan di mana kata kunci berada. Keadaan awal adalah 001 mewakili keadaan bebas kunci. Klassptr menunjuk ke alamat yang diwakili oleh objek yang bytecode kelasnya berada di dalam mesin virtual. Bidang mewakili bidang instance objek kontinu.
4. Monitorrecord:
Monitorrecord adalah struktur data pribadi utas. Setiap utas memiliki daftar monitorecord yang tersedia dan daftar global yang tersedia. Jadi apa saja penggunaan monitorecord ini? Setiap objek yang terkunci akan dikaitkan dengan monitorrecord (kata kunci di header objek menunjuk ke alamat start monitorrecord. Karena alamat ini selaras 8byte, tiga bit terendah kata kunci dapat digunakan sebagai bit status). Pada saat yang sama, ada bidang pemilik di Monitorrecord untuk menyimpan pengidentifikasi unik dari utas yang memiliki kunci, menunjukkan bahwa kunci ditempati oleh utas ini. Gambar berikut menunjukkan struktur internal monitorrecord:
Pemilik: Null di awal berarti bahwa saat ini tidak ada utas yang memiliki catatan monitor. Ketika utas berhasil memiliki kunci, itu menyimpan identitas unik utas, dan ketika kunci dilepaskan, diatur ke nol;
Entryq: Hubungan sistem mutex (semaphore) untuk memblokir semua utas yang gagal mengunci catatan monitor.
RCTHIS: mewakili jumlah semua utas yang diblokir atau menunggu pada catatan monitor.
Nest: Digunakan untuk mengimplementasikan penghitungan kunci masuk kembali.
HashCode: Menghemat nilai kode hashcal yang disalin dari header objek (juga dapat berisi usia GC).
Calon: Digunakan untuk menghindari pemblokiran yang tidak perlu atau menunggu utas bangun, karena hanya satu utas yang berhasil memiliki kunci setiap kali. Jika utas sebelumnya yang melepaskan kunci bangun semua menghalangi atau menunggu utas setiap kali, itu akan menyebabkan switching konteks yang tidak perlu (dari pemblokiran ke siap dan kemudian memblokir lagi karena kegagalan kunci kompetisi) dan dengan demikian menyebabkan degradasi kinerja yang parah. Calon hanya memiliki dua nilai yang mungkin: 0 berarti tidak ada utas yang perlu dibangunkan hingga 1 cara untuk membangunkan utas penerus untuk bersaing untuk kunci.
5. Implementasi spesifik dari kunci ringan:
Benang dapat mengunci objek dengan dua cara: 1. Dapatkan kunci objek dengan memperluas objek dalam keadaan bebas kunci (status bit 001); 2. Objek sudah dalam keadaan diperluas (status bit 00) tetapi bidang pemilik catatan monitor yang ditunjukkan oleh lockword adalah nol, sehingga Anda dapat langsung mencoba mengatur pemiliknya sendiri melalui instruksi atom CAS untuk mendapatkan kunci.
Proses umum mendapatkan kunci (Monitorenter) adalah sebagai berikut:
(1) Ketika objek dalam keadaan bebas kunci (nilai Recordword adalah kode hash, bit status adalah 001), utas pertama memperoleh catatan monitor gratis dari daftar catatan monitor yang tersedia. Nilai sarang dan pemilik awal masing -masing ditetapkan untuk 1 dan identifikasi utas sendiri. Once the monitor record is ready, we install the monitor record's start address to the LockWord field of the object header through the CAS atomic instruction to expand (the original text is inflate. I think the reason why it is called inflate is mainly because the object is expanded after it is expanded; for spatial efficiency, the monitor is used to expand the size of the object; The record structure is extracted from the object header and only attached to the object when needed. However, it is a bit bertentangan dengan makalah ini.
(2) Objek telah diperluas dan utas yang disimpan pada pemilik diidentifikasi sebagai utas yang memperoleh kunci itu sendiri. Ini adalah kasus kunci reentrant. Anda hanya perlu menambahkan 1 ke sarang. Tidak diperlukan operasi atom dan sangat efisien.
(3) Objek telah diperluas tetapi nilai pemiliknya nol. Keadaan ini terjadi ketika benang yang menghalangi atau menunggu pada kunci terkunci pada saat yang sama, pemilik kunci sebelumnya baru saja melepaskan kunci. Pada saat ini, banyak utas mencoba mengatur pemilik ke identitas mereka sendiri melalui instruksi atom CAS untuk mendapatkan kunci dalam keadaan kompetisi multi-utas. Utas yang gagal untuk bersaing akan memasuki jalur eksekusi kasus keempat (4).
(4) Objek berada dalam keadaan yang diperluas dan pemiliknya tidak nol (terkunci). Ini memutar beberapa kali sebelum memanggil mutex kelas berat dari sistem operasi. Ketika beberapa kali tercapai, jika kunci masih belum berhasil diperoleh, sekarang saatnya untuk mulai memasuki keadaan pemblokiran. Pertama, tambahkan nilai rfis secara atom dengan 1. Karena utas lain dapat menghancurkan hubungan antara objek dan catatan monitor selama penambahan 1, perlu melakukan perbandingan lain setelah menambahkan 1 untuk memastikan bahwa nilai kata kunci belum diubah. Ketika ditemukan bahwa itu telah diubah, proses Monitorenter harus diulang. Pada saat yang sama, diamati lagi apakah pemiliknya nol. Jika demikian, itu akan memanggil CAS untuk berpartisipasi dalam kunci kompetisi. Jika kompetisi kunci gagal, itu akan memasuki keadaan pemblokiran.
Proses umum melepaskan kunci (monitorexit) adalah sebagai berikut:
(1) Pertama periksa apakah objek tersebut dalam keadaan diperluas dan utas adalah pemilik kunci. Jika ditemukan bahwa itu salah, pengecualian akan dilemparkan;
(2) Periksa apakah bidang sarang lebih besar dari 1. Jika lebih besar dari 1, cukup kurangi sarang sebesar 1 dan terus memiliki kunci. Jika sama dengan 1, maka masukkan langkah (3);
(3) Periksa apakah RFTHIS lebih besar dari 0, atur pemilik ke NULL dan bangunkan benang pemblokiran atau menunggu untuk mencoba mendapatkan kunci lagi. Jika sama dengan 0, itu akan memasuki langkah (4)
(4) Mengembalikan objek, lepaskan kunci dengan mengganti kata kunci objek kembali ke nilai kode hashcode asli untuk melepaskan kunci dan mengembalikan catatan monitor ke utas.
Meringkaskan
Referensi: " Pemahaman mendalam tentang fitur canggih dan praktik terbaik Java Virtual Machine JVM (Zhou Zhiming) "
Di atas adalah seluruh konten artikel ini tentang analisis masalah yang disinkronkan dan implementasi dari detail JVM. Saya harap ini akan membantu semua orang. Jika ada kekurangan, silakan tinggalkan pesan untuk menunjukkannya. Terima kasih teman atas dukungan Anda untuk situs ini!