Kita tahu bahwa operasi bersamaan yang diterapkan oleh Java akhirnya harus diselesaikan oleh CPU kami. Sementara itu, kami menyusun kode sumber Java ke dalam file .class, kemudian dimuat, dan kemudian dieksekusi oleh mesin eksekusi mesin virtual, ditafsirkan sebagai bahasa perakitan, kemudian dikonversi ke instruksi sistem operasi, kemudian dikonversi menjadi 1, 0, dan akhirnya CPU diakui dan dijalankan.
Ketika kami menyebutkan konkurensi Java, kami tidak dapat membantu tetapi memikirkan kata kunci umum di Java: volatile dan disinkronkan. Selanjutnya, kami akan menganalisisnya dari dua kata shutdown ini:
Prinsip Implementasi Yang Mendasari Volatile
Prinsip Implementasi dan Aplikasi Sinkronisasi
tidak stabil
Berbicara tentang volatile, pewawancara adalah pertanyaan favorit untuk ditanyakan dalam wawancara Java. Ketika kita melihatnya, hal pertama yang kita pikirkan adalah mempertahankan visibilitas di antara benang. Ini adalah sinkronisasi ringan, yang dalam beberapa kasus dapat menggantikan disinkronkan.
Peran volatile:
Untuk variabel yang dimodifikasi dengan volatile, model memori Java akan memastikan bahwa nilai variabel yang terlihat oleh semua utas konsisten.
Bagaimana Volatile Bekerja:
Kami dapat mendefinisikan variabel yang mudah menguap, menetapkan nilai TI, dan menggunakan alat untuk mendapatkan instruksi perakitan yang dihasilkan oleh kompiler JIT. Kami akan menemukan bahwa ketika menulis ke variabel yang mudah menguap, akan ada instruksi tambahan: instruksi diawali dengan kunci:
Instruksi awalan kunci menyebabkan dua hal kembali ke prosesor multi-core:
① Tulis kembali data dari garis cache prosesor saat ini ke memori.
Operasi memori write-back ini akan membatalkan alamat memori yang di-cache data di CPU lainnya.
Ketika kita mengetahui dua poin di atas, tidak sulit bagi kita untuk memahami mekanisme variabel volasi.
Di bawah beberapa prosesor, untuk memastikan bahwa cache masing -masing prosesor konsisten, protokol konsistensi cache akan diimplementasikan. Setiap prosesor mengendus data yang diperbanyak di bus untuk memeriksa apakah nilai yang di -cache telah kedaluwarsa.
disinkronkan
Ketika memikirkan konkurensi multi-utara, hal pertama yang saya pikirkan adalah disinkronkan. Diterjemahkan sebagai sinkronisasi. Kita semua tahu bahwa itu adalah kunci kelas berat. Saat menggunakannya untuk metode atau blok kode, ketika utas mendapatkan kunci ini, utas lain akan jatuh ke dalam keadaan tersuspensi, yang akan muncul dalam keadaan tidur di Java. Kita semua tahu bahwa suspensi dan waktu berjalan utas harus ditransfer ke keadaan kernel dari sistem operasi (yang sesuai dengan keadaan kernel adalah keadaan pengguna), yang sangat boros terhadap sumber daya CPU, sehingga kunci kelas berat ini benar -benar benar -benar!
Namun, setelah Java SE 1.6, tim pemeliharaan Java telah melakukan serangkaian optimasi di atasnya (optimasi ini dibahas satu per satu), jadi bukan itu "berat", dan kunci masuk kembali, yang memiliki kelebihan di masa lalu, menjadi kurang menguntungkan (Reentrantlock).
Mari kita bicara tentang disinkronkan dalam aspek -aspek berikut:
Dasar -dasar disinkronkan untuk mencapai sinkronisasi
Bagaimana Sinkronisasi Implement Lock
Kunci positif, kunci ringan (kunci spin), kunci kelas berat
Upgrade kunci
Cara mengimplementasikan operasi atom di java
① Dasar -dasar disinkronkan untuk mencapai sinkronisasi:
Kita dapat melihat disinkronkan dalam pengembangan atau dalam kode sumber Java, seperti hashtable, stringbuilder dan tempat lain. Ada dua cara umum:
Ⅰ, metode sinkronisasi
Metode sinkronisasi hanya perlu disinkronkan sebelum metode. Ketika satu utas menjalankannya, utas lain akan jatuh ke menunggu sampai melepaskan kunci. Penggunaan metode dapat dibagi menjadi dua jenis: untuk metode sinkronisasi biasa dan untuk metode statis. Perbedaan di antara mereka adalah bahwa objek yang terkunci berbeda. Posisi terkunci dari metode biasa adalah objek saat ini, dan posisi terkunci dari metode statis adalah objek kelas dari kelas saat ini.
Ⅱ, blok metode sinkronisasi
Blok metode sinkronisasi mengunci objek yang dikonfigurasi dalam tanda kurung setelah disinkronkan. Objek ini dapat berupa nilai dan variabel atau objek apa pun.
②Bagaimana Sinkronisasi implementasi Lock:
Dalam spesifikasi JVM, Anda dapat melihat prinsip implementasi disinkronkan dalam JVM. JVM mengimplementasikan sinkronisasi metode sinkronisasi dan blok kode berdasarkan masuk dan keluar dari objek monitor. Blok kode diimplementasikan menggunakan instruksi MonitorENTER dan Monitorexit. Metode sinkronisasi tidak secara khusus diberikan dalam spesifikasi JVM. Namun, saya percaya bahwa prinsip -prinsip spesifik harus berbeda. Ini tidak lebih dari menyusun kode sumber Java ke dalam file kelas, dan menandai metode yang disinkronkan dalam file bytecode kelas. Metode ini akan disinkronkan ketika mesin bytecode menjalankan metode ini.
③ Kunci Defleksi, Kunci Ringan (Kunci SPIN), Kunci Kelas Berat:
Sebelum berbicara tentang kunci, kita perlu mengetahui header objek Java dan header objek Java:
Kunci yang digunakan oleh disinkronkan disimpan di header objek Java. Header objek Java memiliki 32bit/64bit (tergantung pada jumlah bit sistem operasi) panjang Markword menyimpan kode hashcode dan kunci objek. Ada ruang 2bit di Markword untuk mewakili keadaan kunci 00, 01, 10, 11, masing -masing, mewakili kunci ringan, kunci bias, kunci kelas berat, dan tanda GC.
Kunci Positif: Kunci positif disebut kunci eksentrik. Dari nama itu kita dapat melihat bahwa itu adalah kunci yang cenderung ke utas tertentu.
Dalam pengembangan aktual, kami menemukan bahwa konkurensi multi-thread, sebagian besar metode sinkronisasi dilakukan oleh utas yang sama, dan probabilitas beberapa utas yang bersaing untuk satu metode relatif rendah, sehingga akuisisi yang diulang dan pelepasan kunci akan menyebabkan banyak limbah sumber daya. Oleh karena itu, untuk membuat utas mendapatkan kunci dengan biaya yang lebih rendah, kunci bias diperkenalkan. Ketika utas mengakses blok sinkronisasi dan memperoleh kunci, ID utas dari kunci bias akan disimpan dalam catatan kunci di bingkai tumpukan header dan utas objek. Di masa depan, ketika utas masuk dan keluar dari blok sinkronisasi, tidak perlu melakukan operasi CAS untuk mengunci dan membuka kunci. Hanya perlu untuk hanya memeriksa apakah ada kunci bias yang menunjuk ke Markword saat ini di header objek (di Markword, ada bit bendera kunci bias untuk menunjukkan apakah objek saat ini mendukung kunci bias. Kita dapat menggunakan parameter JVM untuk mengatur kunci bias).
Mengenai pelepasan kunci yang bias, kunci yang bias menggunakan mekanisme melepaskan kunci sampai ada kompetisi, sehingga benang yang memegang kunci bias akan melepaskan kunci ketika utas lain mencoba untuk bersaing untuk kunci bias.
CATATAN: Di Java6, 7, Bias Lock dimulai secara default
Kunci ringan:
Kunci ringan adalah bahwa sebelum menjalankan blok sinkronisasi, JVM akan membuat ruang untuk menyimpan catatan kunci di bingkai tumpukan utas saat ini, dan menyalin Markword di header objek ke dalamnya. Kemudian utas akan mencoba mengganti Markword di header objek dengan pointer ke catatan kunci. Jika berhasil, utas saat ini memperoleh kunci. Jika gagal, itu berarti bahwa utas lain bersaing untuk kunci, dan utas saat ini akan berputar untuk mendapatkan kunci.
④ Upgrade kunci:
Jika utas saat ini tidak dapat mencoba metode di atas untuk mendapatkan kunci, itu berarti bahwa kunci saat ini dalam kompetisi dan kunci akan ditingkatkan ke kunci kelas berat.
Perbedaan antara kunci ringan dan kunci bias:
Kunci ringan menggunakan operasi CAS untuk menghilangkan mutex yang digunakan dalam sinkronisasi tanpa kompetisi, sementara kunci bias menghilangkan seluruh sinkronisasi tanpa kompetisi tanpa kompetisi, dan bahkan operasi CAS tidak dilakukan!
⑤ Cara menerapkan operasi atom di Java:
Sebelum memahami bagaimana Java mengimplementasikan operasi atom, kita perlu tahu bagaimana prosesor menerapkan operasi atom:
Prosesor umumnya dibagi menjadi dua cara untuk melakukan operasi atom: penguncian cache dan penguncian bus, di antaranya penguncian cache lebih baik, sementara penguncian bus lebih memakan sumber daya. (Kami tidak akan menjelaskan terlalu banyak tentang dua metode penguncian di sini, tetapi akan ada penjelasan rinci dalam sistem operasi)
Java menggunakan (kebanyakan) Loop CA untuk mengimplementasikan operasi atom, tetapi menggunakan CAS untuk mengimplementasikan operasi atom juga akan menyebabkan beberapa masalah klasik berikut:
1) Masalah ABA
Kelas AtomicstampedReference disediakan di JDK untuk diselesaikan (memberikan referensi yang diharapkan dan bendera yang diharapkan)
2) Waktu siklus panjang dan overhead tinggi
Tidak dapat menyelesaikan ini, ini adalah masalah umum sirkulasi
3) Hanya operasi atom dari variabel bersama yang dapat dijamin
Referensi atomik disediakan dalam JDK untuk menyelesaikan masalah, menempatkan beberapa variabel bersama di kelas untuk operasi CAS.