1. Kunci kelas berat
Dalam artikel sebelumnya, kami memperkenalkan penggunaan prinsip -prinsip implementasinya. Sekarang kita harus tahu bahwa disinkronkan diimplementasikan melalui kunci monitor di dalam objek. Namun, kunci monitor pada dasarnya diimplementasikan dengan mengandalkan kunci mutex sistem operasi yang mendasarinya. Sistem operasi perlu beralih di antara utas dari keadaan pengguna ke status inti. Ini sangat mahal, dan konversi antar negara membutuhkan waktu yang relatif lama. Inilah sebabnya mengapa disinkronkan tidak efisien. Oleh karena itu, kami menyebut kunci ini yang bergantung pada implementasi sistem operasi mutex lock "kunci kelas berat". Inti dari berbagai optimisasi yang dibuat untuk disinkronkan dalam JDK adalah untuk mengurangi penggunaan kunci kelas berat ini. Setelah JDK1.6, untuk mengurangi konsumsi kinerja yang disebabkan oleh memperoleh dan melepaskan kunci dan meningkatkan kinerja, "kunci ringan" dan "kunci bias" diperkenalkan.
2. Kunci ringan
Ada empat jenis status kunci: status bebas kunci, kunci bias, kunci ringan dan kunci kelas berat. Dengan kompetisi kunci, kunci dapat ditingkatkan dari kunci bias ke kunci ringan, dan kemudian meningkatkan kunci kelas berat (tetapi peningkatan kunci adalah satu arah, yang berarti bahwa mereka hanya dapat meningkatkan dari rendah ke tinggi, dan tidak akan ada degradasi kunci). Di JDK 1.6, kunci bias dan kunci ringan diaktifkan secara default. Kami juga dapat menonaktifkan Bias Lock oleh -xx: -UseBiasedlocking. Status kunci disimpan dalam file header objek, mengambil JDK 32-bit sebagai contoh:
Status kunci | 25 bit | 4bit | 1bit | 2bit | ||
23bit | 2bit | Apakah itu kunci bias? | Bit bendera kunci | |||
Kunci ringan | Pointer untuk mengunci catatan di tumpukan | 00 | ||||
Kunci kelas berat | Pointer to mutex (kunci kelas berat) | 10 | ||||
Tag GC | batal | 11 | ||||
Kunci positif | ID utas | Masa | Subjek sudah berumur | 1 | 01 | |
Tanpa kunci | Hashcode of Object | Subjek sudah berumur | 0 | 01 | ||
"Ringan" relatif terhadap kunci tradisional yang menggunakan mutex sistem operasi. Namun, penting untuk menekankan terlebih dahulu bahwa kunci ringan tidak digunakan untuk menggantikan kunci kelas berat. Niat aslinya adalah untuk mengurangi konsumsi kinerja yang dihasilkan oleh penggunaan kunci kelas berat tradisional tanpa kompetisi multi-utara. Sebelum menjelaskan proses eksekusi kunci ringan, pertama -tama kami memahami bahwa skenario yang disesuaikan dengan kunci ringan adalah kasus di mana utas secara bergantian menjalankan blok sinkron. Jika kunci yang sama diakses secara bersamaan, kunci ringan akan diperluas ke kunci kelas berat.
1. Proses penguncian kunci ringan
(1) Ketika kode memasuki blok sinkronisasi, jika status kunci dari objek sinkronisasi bebas kunci (bendera kunci adalah keadaan "01", apakah itu kunci yang bias adalah "0"), mesin virtual akan membuat ruang yang disebut catatan kunci dalam bingkai tumpukan dari utas saat ini untuk menyimpan salinan kata tanda objek kunci saat ini, yang secara resmi disebut dengan kata-kata. Pada saat ini, keadaan tumpukan utas dan header objek ditunjukkan pada Gambar 2.1.
(2) Salin kata tanda di header objek dan salin ke catatan kunci.
(3) Setelah salinan berhasil, mesin virtual akan menggunakan operasi CAS untuk mencoba memperbarui kata tanda objek ke pointer untuk mengunci rekaman, dan arahkan pointer pemilik di catatan kunci ke kata tanda objek. Jika pembaruan berhasil, maka jalankan langkah (3), sebaliknya jalankan langkah (4).
(4) Jika tindakan pembaruan ini berhasil, maka utas memiliki kunci objek, dan bendera kunci dari kata tanda objek diatur ke "00", yang berarti bahwa objek tersebut dalam keadaan terkunci yang ringan. Pada saat ini, keadaan tumpukan utas dan kepala objek ditunjukkan pada Gambar 2.2.
(5) Jika operasi pembaruan ini gagal, mesin virtual akan terlebih dahulu memeriksa apakah kata tanda objek menunjuk ke bingkai tumpukan utas saat ini. Jika demikian, itu berarti bahwa utas saat ini sudah memiliki kunci objek, dan kemudian dapat secara langsung memasukkan blok sinkronisasi untuk melanjutkan eksekusi. Jika tidak, beberapa utas bersaing untuk kunci, dan kunci ringan akan diperluas menjadi kunci kelas berat, dan nilai status bendera kunci akan menjadi "10". Pointer ke kunci kelas berat (mutex) disimpan di Mark Word, dan utas yang menunggu kunci juga akan memasuki status pemblokiran. Utas saat ini mencoba menggunakan putaran untuk mendapatkan kunci. Putarannya adalah untuk menghindari memblokir utas dan menggunakan loop untuk mendapatkan kunci.
Gambar 2.1 Keadaan tumpukan dan objek sebelum operasi CAS kunci ringan
Gambar 2.2 Keadaan tumpukan dan objek setelah operasi CAS kunci ringan
2. Membuka Kunci Kunci Kunci Ringan:
(1) Cobalah untuk mengganti objek kata tanda yang dipindahkan yang disalin dalam utas melalui operasi CAS.
(2) Jika penggantian berhasil, seluruh proses sinkronisasi akan selesai.
(3) Jika penggantian gagal, itu berarti bahwa utas lain telah mencoba untuk mendapatkan kunci (kunci telah diperluas pada saat ini), maka benang yang ditangguhkan harus dibangunkan saat melepaskan kunci.
3. Kunci positif
Pengenalan Kunci Bias adalah untuk meminimalkan jalur eksekusi kunci ringan yang tidak perlu tanpa persaingan multi-thread, karena akuisisi dan pelepasan kunci ringan tergantung pada beberapa instruksi atom CAS, sementara kunci bias hanya perlu mengandalkan satu instruksi yang harus dibatalkan, karena pembersihan bias yang dibatalkan. konsumsi instruksi atom CAS). Seperti disebutkan di atas, kunci ringan digunakan untuk meningkatkan kinerja ketika utas secara bergantian menjalankan blok sinkron, sementara kunci bias digunakan untuk lebih meningkatkan kinerja ketika hanya satu utas yang menjalankan blok sinkron.
1. Proses akuisisi kunci yang bias:
(1) Akses apakah bendera dari kunci bias dalam kata tanda diatur ke 1, dan apakah bendera kunci 01 - konfirmasi bahwa itu adalah keadaan bias yang dapat dibiusi.
(2) Jika itu adalah keadaan bias, uji apakah ID utas menunjuk ke utas saat ini. Jika ya, masukkan langkah (5), jika tidak masukkan langkah (3).
(3) Jika ID utas tidak mengarah ke utas saat ini, maka kunci akan bersaing melalui operasi CAS. Jika kompetisi berhasil, atur ID utas di Mark Word ke ID utas saat ini dan jalankan (5); Jika kompetisi gagal, eksekusi (4).
(4) Jika CAS gagal mendapatkan kunci bias, itu berarti ada persaingan. Ketika SafePoint global tercapai, utas yang memperoleh kunci bias ditangguhkan. Kunci bias ditingkatkan ke kunci ringan, dan utas yang diblokir di SafePoint terus menjalankan kode sinkronisasi.
(5) Jalankan kode sinkronisasi.
2. Pelepasan Kunci Bias:
Pencabutan kunci bias disebutkan pada langkah keempat di atas. Kunci bias hanya akan melepaskan kunci ketika utas lain mencoba untuk bersaing untuk kunci yang bias, dan utas tidak akan secara aktif melepaskan kunci yang bias. Pembatalan kunci yang bias membutuhkan menunggu titik keamanan global (tidak ada bytecode yang dieksekusi pada titik waktu ini). Ini pertama -tama akan menjeda utas dengan kunci yang bias, menentukan apakah objek kunci dalam keadaan terkunci, dan kemudian kembali ke yang tidak terkunci (bit bendera adalah "01") atau kunci ringan (bit flag adalah "00") setelah membatalkan kunci yang bias.
3. Konversi antara kunci kelas berat, kunci ringan dan kunci bias
Gambar 2.3 Diagram konversi dari ketiganya
Gambar ini terutama merupakan ringkasan dari konten di atas. Jika Anda memiliki pemahaman yang baik tentang konten di atas, gambar harus mudah dimengerti.
4. Optimalisasi lainnya
1. Adaptive Spinning: Dari proses mendapatkan kunci ringan, kita tahu bahwa ketika utas gagal melakukan operasi CAS selama akuisisi kunci ringan, perlu untuk mendapatkan kunci kelas berat melalui putaran. Masalahnya adalah putaran membutuhkan konsumsi CPU. Jika kunci tidak dapat diperoleh, utas akan berada dalam keadaan berputar dan limbah sumber daya CPU sia -sia. Cara termudah untuk menyelesaikan masalah ini adalah dengan menentukan jumlah putaran, misalnya, biarkan bersepeda 10 kali, dan memasukkan status pemblokiran jika kunci tidak diperoleh. Tapi JDK mengadopsi pendekatan yang lebih pintar - putaran adaptif. Sederhananya, jika utas berhasil, jumlah putaran akan lebih waktu berikutnya, dan jika putaran gagal, jumlah putaran akan dikurangi.
2. Lock Rerepan: Konsep kekasaran kunci harus lebih mudah dipahami, yaitu untuk menggabungkan operasi kunci dan membuka kunci yang terhubung bersama -sama beberapa kali menjadi satu kali, memperluas beberapa kunci kontinu ke dalam kunci dengan kisaran yang lebih besar. Misalnya:
Paket com.paddx.test.string; kelas publik StringBufferTest {StringBuffer StringBuffer = New StringBuffer (); public void append () {StringBuffer.append ("a"); StringBuffer.Append ("B"); StringBuffer.Append ("C"); }}Di sini, setiap kali Anda menghubungi Metode StringBuffer. Lappend, penguncian dan pembukaan kunci diperlukan. Jika mesin virtual mendeteksi serangkaian operasi penguncian dan membuka kunci pada objek yang sama, ia akan menggabungkannya menjadi beragam operasi penguncian dan pembukaan yang lebih besar, yaitu penguncian dilakukan pada metode penambahan pertama, dan membuka kunci dilakukan setelah metode Append terakhir selesai.
3. Penghapusan kunci: Penghapusan kunci berarti menghilangkan operasi penguncian yang tidak perlu. Menurut teknologi Code Escape, jika ditentukan bahwa sepotong kode dan data pada tumpukan tidak akan lepas dari utas saat ini, maka dapat dipertimbangkan bahwa potongan kode ini aman dan tidak perlu menguncinya. Lihatlah program berikut:
Paket com.paddx.test.concurrent; kelas publik SynchronizedTest02 {public static void main (string [] args) {synchronizedTest02 test02 = new SynchronizedTest02 (); // Mulailah pemanasan untuk (int i = 0; i <10000; i ++) {i ++; } Long start = system.currentTimeMillis (); untuk (int i = 0; i <100000000; i ++) {test02.append ("abc", "def"); } System.out.println ("Time =" + (System.CurrentTimeMillis () - start)); } public void append (string str1, string str2) {stringBuffer sb = new stringBuffer (); SB.Append (str1) .Append (str2); }}Meskipun Append of StringBuffer adalah metode sinkron, StringBuffer dalam program ini termasuk dalam variabel lokal dan tidak akan lepas dari metode ini. Oleh karena itu, proses ini sebenarnya aman-utas dan dapat menghilangkan kunci. Inilah hasil dari eksekusi lokal saya:
Untuk meminimalkan dampak faktor-faktor lain, Bias Lock (-xx: -UseBiasedlocking) dinonaktifkan di sini. Melalui program di atas, dapat dilihat bahwa kinerja telah sangat ditingkatkan setelah kunci dihilangkan.
Catatan: Hasil eksekusi mungkin berbeda antara versi JDK yang berbeda. Versi JDK yang saya gunakan di sini adalah 1.6.
5. Ringkasan
Artikel ini berfokus pada optimalisasi yang disinkronkan dalam JDK, seperti kunci ringan dan kunci yang bias, tetapi kedua kunci ini tidak sepenuhnya tanpa kekurangan. Misalnya, ketika persaingan sengit, ia tidak hanya akan gagal meningkatkan efisiensi, tetapi juga akan mengurangi efisiensi, karena ada proses peningkatan kunci tambahan. Pada saat ini, perlu untuk menonaktifkan kunci yang bias melalui -xx: -UseBiasedlocking. Berikut adalah perbandingan dari kunci ini:
Kunci | keuntungan | kekurangan | Skenario yang berlaku |
Kunci positif | Mengunci dan membuka kunci tidak memerlukan konsumsi tambahan, dan ada celah nanosecond dibandingkan dengan melakukan metode asinkron. | Jika ada persaingan kunci antar utas, itu akan menyebabkan konsumsi pencabutan kunci tambahan. | Cocok untuk skenario di mana hanya satu utas mengakses blok sinkron. |
Kunci ringan | Thread yang bersaing tidak akan memblokir, yang meningkatkan kecepatan respons program. | Jika utas yang tidak pernah mendapatkan kompetisi kunci akan mengkonsumsi CPU. | Mengejar waktu respons. Eksekusi blok sinkron sangat cepat. |
Kunci kelas berat | Persaingan utas tidak menggunakan putaran dan tidak mengonsumsi CPU. | Pemblokiran utas, waktu respons yang lambat. | Mengejar throughput. Kecepatan eksekusi blok sinkronisasi relatif panjang. |