Rumah >Java >javaTutorial >Apakah pelbagai kunci di Jawa?
Pengenalan kepada 15 jenis kunci dalam Java
Dalam membaca banyak artikel konkurensi, pelbagai kunci akan disebut, seperti kunci saksama, kunci optimis, dll. Artikel ini memperkenalkan klasifikasi pelbagai kunci. Kandungan pengenalan adalah seperti berikut:
Kunci adil/kunci tidak adil
Kunci masuk semula / kunci bukan masuk semula
Kunci eksklusif / kunci kongsi
Kunci mutex / kunci baca-tulis
Kunci Optimis/Kunci Pesimis
Kunci segmen
Kunci berat sebelah / kunci ringan / kunci berat
Kunci putaran
Di atas adalah banyak kata nama kunci Klasifikasi ini tidak semua merujuk kepada status kunci Ada yang merujuk kepada ciri kunci, dan ada yang merujuk kepada reka bentuk kunci kata nama.
Kunci adil/kunci tidak adil
Kunci Adil
Kunci saksama bermakna berbilang benang memperoleh kunci mengikut urutan ia memohon kunci.
Kunci tidak adil
Kunci tidak adil bermakna bahawa urutan berbilang benang memperoleh kunci tidak mengikut urutan yang digunakan untuk kunci Ada kemungkinan benang yang digunakan kemudian memperoleh kunci sebelum benang yang digunakan dahulu. Ada kemungkinan ia akan menyebabkan penyongsangan keutamaan atau kebuluran.
Untuk Java ReentrantLock, anda menentukan sama ada kunci itu adalah kunci adil melalui pembina, dan lalainya ialah kunci tidak adil. Kelebihan kunci tidak adil ialah daya pemprosesan lebih besar daripada kunci adil. Untuk Disegerakkan, ia juga merupakan kunci yang tidak adil. Memandangkan ia tidak melaksanakan penjadualan benang melalui AQS seperti ReentrantLock, tidak ada cara untuk mengubahnya menjadi kunci yang adil.
Kunci masuk semula / kunci bukan masuk semula
Kunci masuk semula
Kunci reentrant dalam erti kata luas merujuk kepada kunci yang boleh dipanggil berulang kali dan secara rekursif Selepas kunci digunakan di lapisan luar, ia masih boleh digunakan di lapisan dalam tanpa kebuntuan (dengan syarat ia adalah objek atau kelas yang sama. ). Kunci sedemikian Ia dipanggil kunci masuk semula. ReentrantLock dan disegerakkan adalah kedua-duanya kunci reentrant
setA() void disegerakkan membuang Exception{
Thread.sleep(1000);
setB();
}
setB() void disegerakkan membuang Exception{
Thread.sleep(1000);
}
Kod di atas ialah ciri kunci reentrant Jika ia bukan kunci reentrant, setB mungkin tidak dilaksanakan oleh thread semasa, yang boleh menyebabkan kebuntuan.
Kunci tidak boleh masuk semula
Kunci bukan masuk semula, bertentangan dengan kunci masuk semula, tidak boleh dipanggil secara rekursif, dan kebuntuan akan berlaku jika panggilan rekursif dibuat. Saya melihat penjelasan klasik tentang menggunakan kunci putaran untuk mensimulasikan kunci bukan masuk semula Kodnya adalah seperti berikut
import java.util.concurrent.atomic.AtomicReference;
kelas awam UnreentrantLock {
AtomicReference
kunci kekosongan awam() {
Arus benang = Thread.currentThread();
//Ayat ini ialah sintaks "spin" yang sangat klasik, dan terdapat juga
dalam AtomicInteger untuk (;;) {
jika (!owner.compareAndSet(null, current)) {
kembali;
}
}
}
buka kunci kekosongan awam() {
Arus benang = Thread.currentThread();
owner.compareAndSet(current, null);
}
}
Kod ini juga agak mudah. Ia menggunakan rujukan atom untuk menyimpan benang yang sama memanggil kaedah lock() dua kali Jika buka kunci() tidak dilaksanakan untuk melepaskan kunci, kebuntuan akan berlaku apabila putaran dipanggil untuk kedua. masa. Kunci ini bukan Ia masuk semula, tetapi sebenarnya benang yang sama tidak perlu melepaskan kunci dan memperoleh kunci setiap kali Pensuisan penjadualan sedemikian sangat memakan sumber.
Tukarkannya menjadi kunci masuk semula:
import java.util.concurrent.atomic.AtomicReference;
kelas awam UnreentrantLock {
AtomicReference
keadaan int peribadi = 0;
kunci kekosongan awam() {
Arus benang = Thread.currentThread();
jika (semasa == owner.get()) {
negeri++;
kembali;
}
//Ayat ini ialah sintaks "spin" yang sangat klasik, dan terdapat juga
dalam AtomicInteger untuk (;;) {
jika (!owner.compareAndSet(null, current)) {
kembali;
}
}
}
buka kunci kekosongan awam() {
Arus benang = Thread.currentThread();
jika (semasa == owner.get()) {
jika (nyatakan != 0) {
negeri--;
} lain {
owner.compareAndSet(semasa, nol);
}
}
}
}
Sebelum melakukan setiap operasi, tentukan sama ada pemegang kunci semasa ialah objek semasa dan gunakan pengiraan keadaan dan bukannya melepaskan kunci setiap kali.
Pelaksanaan kunci reentrant dalam ReentrantLock
Berikut ialah kaedah pemerolehan kunci untuk kunci tidak adil:
akhir boolean nonfairTryAcquire(int memperoleh) {
Arus benang akhir = Thread.currentThread();
int c = getState();
jika (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(semasa);
kembali benar;
}
}
//Ini dia
else if (semasa == getExclusiveOwnerThread()) {
int nextc = c + memperoleh;
if (nextc < 0) // limpahan
Campak Ralat baharu("Melebihi bilangan kunci maksimum");
setState(nextc);
kembali benar;
}
pulangkan palsu;
}
Keadaan int tidak menentu peribadi dikekalkan dalam AQS untuk mengira bilangan kemasukan semula dan mengelakkan operasi penahanan dan pelepasan yang kerap, yang bukan sahaja meningkatkan kecekapan tetapi juga mengelakkan kebuntuan.
Kunci eksklusif / kunci kongsi
Kunci eksklusif dan kunci kongsi Apabila anda membaca ReeReentrantLock dan ReentrantReadWriteLock di bawah pakej C.U.T, anda akan mendapati bahawa salah satu daripadanya ialah kunci eksklusif dan satu lagi kunci kongsi.
Kunci eksklusif: Kunci ini hanya boleh dipegang oleh satu utas pada satu masa.
Kunci kongsi: Kunci ini boleh dikongsi oleh berbilang urutan Contoh biasa ialah kunci baca dalam ReentrantReadWriteLock Kunci bacanya boleh dikongsi, tetapi kunci tulisnya hanya boleh eksklusif pada satu masa.
Di samping itu, perkongsian kunci baca boleh memastikan bahawa bacaan serentak adalah sangat cekap, tetapi membaca dan menulis, menulis dan membaca adalah saling eksklusif.
Kunci eksklusif dan kunci kongsi juga dilaksanakan melalui AQS, dan kaedah berbeza dilaksanakan untuk mencapai kunci eksklusif atau kongsi. Untuk Synchronized, sudah tentu ia adalah kunci eksklusif.
Kunci mutex / kunci baca-tulis
Kunci Mutex
Kunci sumber kongsi sebelum mengaksesnya dan buka kuncinya selepas akses selesai. Selepas mengunci, sebarang utas lain yang cuba dikunci semula akan disekat sehingga proses semasa dibuka.
Jika lebih daripada satu utas disekat semasa membuka kunci, maka semua utas pada kunci akan diprogramkan ke dalam keadaan sedia. Dengan cara ini, hanya satu utas boleh mengakses sumber yang dilindungi oleh mutex
Kunci baca-tulis
Kunci baca-tulis ialah kunci mutex dan kunci kongsi Mod baca dikongsi dan mod tulis adalah saling eksklusif (kunci eksklusif).
Terdapat tiga keadaan kunci baca-tulis: keadaan baca-kunci, keadaan tulis-kunci dan keadaan tidak terkunci
Pelaksanaan khusus kunci baca-tulis dalam Java ialah ReadWriteLock
Hanya satu utas boleh memegang kunci baca-tulis dalam mod tulis pada satu masa, tetapi beberapa utas boleh memegang kunci baca-tulis dalam mod baca pada masa yang sama. Hanya satu utas boleh menduduki kunci keadaan tulis, tetapi beberapa utas boleh menduduki kunci keadaan baca pada masa yang sama, itulah sebabnya ia boleh mencapai keselarasan tinggi. Apabila ia berada di bawah kunci keadaan tulis, mana-mana benang yang ingin mendapatkan kunci akan disekat sehingga kunci keadaan tulis dilepaskan jika ia berada di bawah kunci keadaan baca, utas lain dibenarkan untuk mendapatkan kunci keadaan bacanya, tetapi berada tidak dibenarkan untuk mendapatkan kunci status tulis sehingga kunci status baca semua utas dilepaskan untuk mengelakkan utas yang ingin mencuba operasi menulis daripada mendapatkan kunci status tulis, apabila kunci baca-tulis merasakan yang dikehendaki oleh benang; untuk mendapatkan kunci status tulis, ia akan Sekat semua utas berikutnya yang ingin mendapatkan kunci status baca. Oleh itu, kunci baca-tulis sangat sesuai untuk situasi di mana terdapat lebih banyak operasi baca pada sumber daripada operasi tulis.
Kunci Optimis/Kunci Pesimis
Kunci pesimis
Sentiasa menganggap senario terburuk Setiap kali anda pergi untuk mendapatkan data, anda fikir orang lain akan mengubah suainya, jadi anda akan menguncinya setiap kali anda mendapat data, jika orang lain ingin mendapatkan data itu sekat sehingga mereka mendapat kunci (sumber kongsi Ia hanya digunakan oleh satu utas pada satu masa, utas lain disekat, dan sumber dipindahkan ke utas lain selepas digunakan). Banyak mekanisme kunci sedemikian digunakan dalam pangkalan data hubungan tradisional, seperti kunci baris, kunci meja, kunci baca, kunci tulis, dll., yang semuanya dikunci sebelum operasi. Kunci eksklusif seperti disegerakkan dan ReentrantLock di Jawa adalah pelaksanaan idea kunci pesimis.
Kunci optimistik
Sentiasa menganggap situasi terbaik Setiap kali anda pergi untuk mendapatkan data, anda berfikir bahawa orang lain tidak akan mengubah suainya, jadi ia tidak akan dikunci boleh menggunakan mekanisme Nombor versi dan pelaksanaan algoritma CAS. Penguncian optimis sesuai untuk jenis aplikasi berbilang baca, yang boleh meningkatkan daya pemprosesan Mekanisme keadaan_tulis yang disediakan oleh pangkalan data sebenarnya adalah kunci optimis. Di Java, kelas pembolehubah atom di bawah pakej java.util.concurrent.atomic dilaksanakan menggunakan CAS, kaedah pelaksanaan penguncian optimistik.
Kunci segmen
Kunci bersegmen sebenarnya adalah reka bentuk kunci, bukan kunci khusus Untuk ConcurrentHashMap, pelaksanaan serentaknya adalah untuk mencapai operasi serentak yang cekap dalam bentuk kunci bersegmen.
Mekanisme penguncian kelas kontena serentak adalah berdasarkan kunci tersegmen dengan kebutiran yang lebih kecil Kunci bersegmen juga merupakan salah satu cara penting untuk meningkatkan prestasi program berbilang serentak.
Dalam program serentak, operasi bersiri akan mengurangkan kebolehskalaan, dan penukaran konteks juga akan mengurangkan prestasi. Kedua-dua masalah ini akan berpunca apabila persaingan berlaku pada kunci Apabila menggunakan kunci eksklusif untuk melindungi sumber terhad, ia pada asasnya adalah kaedah bersiri - hanya satu utas boleh mengaksesnya pada satu masa. Jadi ancaman terbesar kepada skalabiliti ialah kunci eksklusif.
Kami biasanya mempunyai tiga cara untuk mengurangkan tahap persaingan kunci: 1. Kurangkan masa memegang kunci 2. Kurangkan kekerapan permintaan kunci 3. Gunakan kunci eksklusif dengan mekanisme penyelarasan.
Dalam sesetengah kes, kita boleh melanjutkan lagi teknik penguraian kunci untuk menguraikan kunci pada set objek bebas, yang menjadi kunci bersegmen.
Sebenarnya, secara ringkasnya:
Terdapat berbilang kunci dalam bekas, dan setiap kunci digunakan untuk mengunci sebahagian daripada data dalam bekas Kemudian apabila berbilang benang mengakses data dalam segmen data yang berbeza dalam bekas, tidak akan ada persaingan kunci antara utas, yang boleh berkesan. meningkatkan kecekapan akses serentak , ini ialah teknologi pembahagian kunci yang digunakan oleh ConcurrentHashMap Pertama, data dibahagikan kepada segmen untuk storan, dan kemudian kunci diberikan kepada setiap segmen data segmen data, data segmen lain juga boleh diakses oleh utas lain.
Sebagai contoh: Tatasusunan yang mengandungi 16 kunci digunakan dalam ConcurrentHashMap Setiap kunci melindungi 1/16 daripada semua baldi cincang dan baldi cincang Nth dilindungi oleh kunci (N mod 16). Dengan mengandaikan bahawa algoritma pencincangan yang munasabah digunakan untuk mengedarkan kunci secara sama rata, ini secara kasar boleh mengurangkan permintaan kunci kepada 1/16. Teknologi inilah yang membolehkan ConcurrentHashMap menyokong sehingga 16 utas penulisan serentak.
Kunci berat sebelah / kunci ringan / kunci berat
Status kunci:
Status tidak berkunci
Status kunci berat sebelah
Status kunci ringan
Status kunci kelas berat
Status kunci ditunjukkan oleh medan dalam pengepala objek monitor objek. Keempat-empat negeri itu secara beransur-ansur akan meningkat dengan persaingan, dan ia adalah proses yang tidak dapat dipulihkan, iaitu, ia tidak boleh diturunkan. Empat keadaan ini bukan kunci dalam bahasa Java, tetapi pengoptimuman yang dibuat oleh Jvm untuk meningkatkan kecekapan pemerolehan dan pelepasan kunci (apabila menggunakan disegerakkan).
Kunci berat sebelah
Kunci berat sebelah bermaksud bahawa sekeping kod penyegerakan sentiasa diakses oleh satu utas, maka utas itu akan memperoleh kunci secara automatik. Kurangkan kos untuk mendapatkan kunci.
Ringan
Kunci ringan bermakna apabila kunci itu adalah kunci berat sebelah dan diakses oleh utas lain, kunci berat sebelah akan dinaik taraf kepada kunci ringan Ulir lain akan cuba memperoleh kunci melalui putaran, yang tidak akan menyekat dan meningkatkan prestasi .
Kunci berat
Kunci kelas berat bermakna apabila kunci itu adalah kunci ringan, walaupun benang lain berputar, putaran tidak akan berterusan apabila ia berputar beberapa kali dan kunci belum diperoleh, ia akan memasuki sekatan , kunci mengembang menjadi kunci kelas berat. Kunci berat akan menyekat utas aplikasi lain dan mengurangkan prestasi.
Kunci putaran
Kami tahu bahawa algoritma CAS ialah kaedah pelaksanaan penguncian optimistik Algoritma CAS juga melibatkan kunci putaran, jadi di sini saya akan memberitahu anda apa itu kunci putaran.
Kajian ringkas tentang algoritma CAS
CAS ialah perkataan Inggeris Compare and Swap (Compare and Swap), yang merupakan algoritma bebas kunci yang terkenal. Pengaturcaraan tanpa kunci bermaksud menyegerakkan pembolehubah antara berbilang utas tanpa menggunakan kunci, iaitu menyegerakkan pembolehubah tanpa utas disekat, jadi ia juga dipanggil Penyegerakan Tidak Menghalang. Algoritma CAS melibatkan tiga operan
Nilai ingatan yang perlu dibaca dan ditulis V
Nilai yang hendak dibandingkan A
Nilai baharu yang akan ditulis B
Apabila mengemas kini pembolehubah, hanya apabila nilai jangkaan A pembolehubah adalah sama dengan nilai sebenar dalam alamat memori V, nilai yang sepadan dengan alamat memori V akan diubah suai kepada B, jika tidak, tiada operasi akan dilakukan. Secara amnya, ia adalah operasi putaran, iaitu percubaan semula berterusan.
Apakah kunci putaran?
Kunci putaran (kunci putaran): Apabila benang memperoleh kunci, jika kunci telah diperoleh oleh benang lain, maka benang akan menunggu dalam satu gelung, dan kemudian terus menilai sama ada kunci itu boleh berjaya diperoleh sehingga kunci diperoleh. Keluar dari gelung.
Ia adalah mekanisme kunci yang dicadangkan untuk melindungi sumber yang dikongsi. Sebenarnya, kunci putaran adalah serupa dengan kunci mutex Kedua-duanya direka untuk menyelesaikan penggunaan sumber tertentu yang saling eksklusif. Sama ada kunci mutex atau kunci putaran, boleh ada paling banyak satu pemegang pada bila-bila masa Dalam erti kata lain, paling banyak satu unit pelaksanaan boleh mendapatkan kunci pada bila-bila masa. Tetapi kedua-duanya mempunyai mekanisme penjadualan yang sedikit berbeza. Untuk kunci mutex, jika sumber sudah diduduki, pemohon sumber hanya boleh memasuki keadaan tidur. Walau bagaimanapun, kunci putaran tidak akan menyebabkan pemanggil tidur Jika kunci putaran telah dipegang oleh unit pelaksanaan yang lain, pemanggil akan terus menggelung di sana untuk melihat sama ada pemegang kunci putaran telah melepaskan kunci. Itulah sebabnya ia mendapat namanya.
Bagaimana untuk melaksanakan kunci putaran di Jawa?
Berikut ialah contoh mudah:
SpinLock kelas awam {
AtomicReference
kunci kekosongan awam() {
Arus benang = Thread.currentThread();
//Gunakan CAS
sementara (!cas.compareAndSet(null, current)) {
// BUAT apa-apa
}
}
buka kunci kekosongan awam() {
Arus benang = Thread.currentThread();
cas.compareAndSet(semasa, nol);
}
}
Kaedah kunci() menggunakan CAS Apabila utas pertama A memperoleh kunci, ia boleh memperolehnya dengan jayanya dan tidak akan memasuki gelung sementara Jika utas A tidak melepaskan kunci pada masa ini, utas lain B akan memperoleh kunci itu semula . Pada masa ini Memandangkan CAS tidak berpuas hati, ia akan memasuki gelung sementara dan secara berterusan menilai sama ada CAS berpuas hati sehingga utas A memanggil kaedah buka kunci untuk melepaskan kunci.
Masalah dengan kunci putaran
1. Jika benang memegang kunci terlalu lama, ia akan menyebabkan benang lain yang menunggu untuk memperoleh kunci memasuki gelung dan menggunakan CPU. Penggunaan yang tidak betul boleh menyebabkan penggunaan CPU yang sangat tinggi. 2. Kunci putaran yang dilaksanakan di Jawa di atas adalah tidak adil, iaitu, ia tidak dapat memenuhi benang dengan masa menunggu yang paling lama untuk memperoleh kunci terlebih dahulu. Kunci yang tidak adil akan menyebabkan masalah "kebuluran benang".
Kelebihan kunci putaran
1. Kunci putaran tidak akan menyebabkan keadaan benang bertukar, dan ia akan sentiasa berada dalam keadaan pengguna, iaitu, benang akan sentiasa aktif; , dan kelajuan pelaksanaan akan menjadi cepat 2. Bukan putaran Apabila kunci tidak dapat diperoleh, kunci akan memasuki keadaan menyekat dan memasuki keadaan inti Apabila kunci diperoleh, ia perlu dipulihkan daripada keadaan kernel. yang memerlukan penukaran konteks benang. (Selepas benang disekat, ia memasuki keadaan penjadualan kernel (Linux). Ini akan menyebabkan sistem bertukar ke sana ke mari antara mod pengguna dan mod kernel, yang menjejaskan prestasi kunci secara serius)
Kunci putaran masuk semula dan kunci putaran bukan masuk semula
Analisis teliti kod pada permulaan artikel menunjukkan bahawa ia tidak menyokong kemasukan semula, iaitu, apabila benang memperoleh kunci untuk kali pertama, ia memperoleh semula kunci itu sebelum kunci dilepaskan berjaya kali kedua. Memandangkan CAS tidak berpuas hati, pemerolehan kedua akan memasuki gelung sementara dan tunggu Jika ia adalah kunci masuk semula, ia harus berjaya diperoleh untuk kali kedua.
Lebih-lebih lagi, walaupun ia boleh diperoleh dengan jayanya untuk kali kedua, apabila kunci dilepaskan buat kali pertama, kunci yang diperoleh untuk kali kedua juga akan dilepaskan, yang tidak munasabah.
Untuk melaksanakan kunci masuk semula, kami perlu memperkenalkan pembilang untuk merekodkan bilangan utas yang memperoleh kunci.
kelas awam ReentrantSpinLock {
AtomicReference
kiraan int peribadi;
kunci kekosongan awam() {
Arus benang = Thread.currentThread();
if (semasa == cas.get()) { // Jika utas semasa telah memperoleh kunci, tambah bilangan utas sebanyak satu, dan kemudian kembalikan
kira++;
kembali;
}
// Jika kunci tidak diperoleh, putar melalui CAS
sementara (!cas.compareAndSet(null, current)) {
// BUAT apa-apa
}
}
buka kunci kekosongan awam() {
Thread cur = Thread.currentThread();
if (semasa == cas.get()) {
if (count > 0) {//Jika lebih daripada 0, ini bermakna urutan semasa telah memperoleh kunci beberapa kali dan melepaskan kunci disimulasikan dengan mengurangkan kiraan dengan satu
Kira--;
} lain {// Jika dikira==0, kunci boleh dilepaskan, dengan itu memastikan bahawa bilangan kali kunci diperoleh adalah konsisten dengan bilangan kali kunci dilepaskan.
cas.compareAndSet(cur, null);
}
}
}
}
Kunci putaran dan kunci mutex
Kedua-dua kunci putaran dan kunci mutex adalah mekanisme untuk melindungi perkongsian sumber.
Sama ada kunci putaran atau kunci mutex, boleh ada paling banyak satu pemegang pada bila-bila masa.
Jika benang yang memperoleh kunci mutex sudah diduduki, benang akan memasuki keadaan tidur; benang yang memperoleh kunci putaran tidak akan tidur, tetapi akan terus menunggu dalam gelung untuk kunci dilepaskan.
Atas ialah kandungan terperinci Apakah pelbagai kunci di Jawa?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!