Apa saja kunci di java
Saya tidak bisa menjawab pertanyaan ini setelah membaca <Java Concurrent Programming>, yang menunjukkan bahwa saya tidak cukup mengerti tentang konsep kunci. Jadi saya melihat melalui isi buku itu lagi dan tiba -tiba merasa seperti membuka dahiku. Tampaknya cara terbaik untuk belajar adalah belajar dengan masalah dan menyelesaikannya.
Di Java, ada dua jenis kunci utama: kunci internal yang disinkronkan dan tampilan kunci java.util.concurrent.locks.lock. Tetapi jika Anda memikirkannya dengan cermat, tampaknya ringkasannya tidak benar. Itu harus berupa serangkaian kunci yang diimplementasikan oleh kunci bawaan Java dan bersamaan.
Mengapa ini mengatakan, karena di Java semuanya adalah objek, dan Java memiliki kunci yang dibangun ke dalam setiap objek, yang juga dapat disebut kunci objek/kunci internal. Operasi kunci yang relevan diselesaikan melalui disinkronkan.
Karena kelemahan dalam implementasi sinkronisasi dan kompleksitas skenario bersamaan, seseorang telah mengembangkan kunci eksplisit, dan kunci ini berasal dari java.util.concurrent.locks.locks. Tentu saja, telah dibangun ke dalam versi JDK1.5 dan yang lebih baru.
disinkronkan
Pertama, mari kita lihat sinkronisasi yang lebih sering digunakan. Ini juga digunakan dalam pekerjaan sehari -hari saya. Sinkronisasi digunakan untuk memberikan mekanisme kunci untuk blok kode tertentu. Secara implisit akan memiliki kunci di objek Java. Kunci ini disebut kunci intrinsik atau monitor. Utas secara otomatis memperoleh kunci ini sebelum memasukkan blok yang dilindungi oleh disinkronkan, sampai kunci secara otomatis dirilis setelah kode selesai (atau mungkin juga pengecualian). Kunci bawaan saling eksklusif. Kunci hanya dapat dipegang oleh satu utas secara bersamaan, yang juga akan menyebabkan beberapa utas, dan utas di belakang kunci akan diblokir setelah ditahan. Ini memungkinkan keamanan utas kode untuk memastikan atomisitas.
Masuk kembali
Karena kunci bawaan Java saling eksklusif dan utas selanjutnya akan menyebabkan penyumbatan, apa yang terjadi jika utas yang menahan kunci masuk lagi ketika mencoba mendapatkan kunci? Misalnya, salah satu situasi berikut:
kelas publik baseclass {public disinkronkan void do () {System.out.println ("is base"); }} kelas publik sonclass memperluas baseclass {public disinkronkan void do () {System.out.println ("is son"); super.do (); }} Sonclass son = new sonclass (); son.do ();Pada saat ini, metode DO dari kelas yang diturunkan akan pertama -tama memegang kunci sekali, dan kemudian masukkan kunci lagi dan pegang saat memanggil super.do (). Jika kunci saling eksklusif, itu harus menemui jalan buntu saat ini.
Tetapi hasilnya tidak terjadi, karena kunci internal memiliki karakteristik reentrant, yaitu, kunci mengimplementasikan mekanisme masuk kembali, manajemen jumlah referensi. Ketika Thread 1 memegang kunci objek A, referensi untuk mengunci A akan dihitung dengan menambahkan 1. Kemudian ketika Thread 1 memperoleh kunci A lagi, Thread 1 masih memegang kunci A, maka perhitungan akan menambahkan 1. Tentu saja, setiap kali Anda keluar dari blok sinkronisasi, akan dikurangi dengan 1 hingga 0.
Beberapa fitur yang disinkronkan
Cara memodifikasi kode
Metode modifikasi
kelas publik baseclass {public disinkronkan void do () {System.out.println ("is base"); }}Ini berarti mengunci metode secara langsung, dan Anda perlu mendapatkan kunci saat memasuki blok metode ini.
Ubah blok kode
kelas publik baseclass {private static objek lock = objek baru (); public void do () {disinkronkan (lock) {System.out.println ("adalah basis"); }}}Di sini, kisaran kunci dikurangi menjadi beberapa blok kode dalam metode ini, yang meningkatkan fleksibilitas kunci. Bagaimanapun, kontrol granularitas kunci juga merupakan masalah utama untuk kunci.
Jenis kunci objek
Saya sering melihat bahwa beberapa kode menggunakan disinkronkan secara khusus, dan lihat kode berikut:
kelas publik baseclass {private static objek lock = objek baru (); public void do () {disinkronkan (lock) {}} public disinkronkan void dovoid () {} public static void dostaticvoid () {} public static void dostaticvoid () {sinkronisasi (Baseclass.class) {}}}Ada empat situasi di sini: memodifikasi blok kode, memodifikasi metode, memodifikasi metode statis, dan memodifikasi objek kelas baseclass. Jadi apa perbedaan dalam situasi ini?
Ubah blok kode
Dalam hal ini, kami membuat kunci objek, menggunakan sinkronisasi (kunci) dalam kode, yang berarti menggunakan kunci bawaan objek. Dalam hal ini, kontrol kunci diserahkan ke objek. Tentu saja ada cara lain untuk melakukan ini:
public void do () {disinkronkan (this) {System.out.println ("adalah basis"); }}Menggunakan ini berarti kunci objek saat ini. Kunci kunci bawaan juga disebutkan di sini. Saya memberikan kunci untuk melindungi kode ini. Tidak peduli utas mana yang datang, itu akan menghadapi kunci yang sama.
Metode untuk memodifikasi objek
Apa situasi dengan modifikasi langsung ini? Bahkan, ini mirip dengan memodifikasi blok kode, kecuali bahwa ini adalah kunci objek saat ini secara default. Dengan cara ini, relatif sederhana dan jelas untuk menulis kode. Seperti yang disebutkan sebelumnya, perbedaan antara memodifikasi blok kode terutama adalah perbedaan antara granularitas pengendalian.
Ubah metode statis
Apakah ada yang berbeda tentang metode statis? Memang berbeda. Kunci yang diperoleh saat ini tidak lagi ini, dan kelas yang ditunjuk oleh objek ini adalah kunci kelas. Karena informasi kelas di Java akan dimuat ke dalam area konstan metode, global adalah unik. Ini sebenarnya menyediakan kunci global.
Objek kelas dari kelas yang dimodifikasi
Situasi ini sebenarnya sangat mirip dengan saat memodifikasi metode statis, tetapi masih merupakan alasan yang sama. Metode ini dapat memberikan granularitas kontrol yang lebih fleksibel.
ringkasan
Melalui analisis dan pemahaman tentang situasi ini, kita benar-benar dapat melihat bahwa konsep inti utama dari kunci bawaan adalah untuk menyediakan sepotong kode dengan kunci yang dapat digunakan untuk saling eksklusif, dan memainkan fungsi yang mirip dengan sakelar.
Java juga menyediakan beberapa implementasi untuk kunci bawaan. Fitur utama adalah bahwa Java adalah semua objek, dan setiap objek memiliki kunci, sehingga Anda dapat memilih kunci mana yang akan digunakan sesuai dengan situasinya.
java.util.concurrent.locks.lock
Saya melihat sinkronisasi sebelumnya. Dalam kebanyakan kasus, hampir cukup. Namun, sistem ini menjadi semakin kompleks dalam pemrograman bersamaan, jadi selalu ada banyak skenario bahwa pemrosesan yang disinkronkan akan lebih sulit. Atau seperti yang dinyatakan dalam <java concurrent programming>, kunci bersamaan adalah pelengkap kunci internal, memberikan fitur yang lebih canggih.
Analisis sederhana java.util.concurrent.locks.lock
Antarmuka ini mengabstraksi operasi utama kunci, dan dengan demikian memungkinkan kunci yang berasal dari kunci untuk memiliki karakteristik dasar ini: tanpa syarat, dapat dihitung tepat waktu, interruptable. Selain itu, operasi penguncian dan pembuka dilakukan secara eksplisit. Ini kodenya:
kunci antarmuka publik {void lock (); void lockerTericly () melempar interrupted exception; boolean trylock (); Boolean Trylock (lama, unit TimeUnit) melempar interruptedException; void unlock (); Kondisi newcondition ();} Reentrantlock
Reentrantlock adalah kunci reentrant, bahkan namanya sangat eksplisit. Reentrantlock menyediakan semantik serupa untuk disinkronkan, tetapi reentrantlock harus disebut secara eksplisit, seperti:
kelas publik baseclass {private lock lock = baru reentrantlock (); public void do () {lock.lock (); coba {// ..} akhirnya {lock.unlock (); }}}Metode ini cukup jelas untuk pembacaan kode, tetapi ada masalah, yaitu, jika Anda lupa menambahkan coba akhirnya atau lupa untuk menulis lock.unlock (), itu akan menyebabkan kunci tidak dirilis, yang dapat menyebabkan beberapa kebuntuan. Tidak ada risiko disinkronkan.
Trylock
Reentrantlock mengimplementasikan antarmuka kunci, sehingga secara alami memiliki fitur -fiturnya, termasuk Trylock. Trylock adalah mencoba memperoleh kunci. Jika kunci telah ditempati oleh utas lain, itu akan segera mengembalikan false. Jika tidak, itu harus ditempati dan dikembalikan benar, yang berarti bahwa kunci telah diperoleh.
Metode trylock lain berisi parameter. Fungsi dari metode ini adalah untuk menentukan waktu, yang berarti Anda terus mencoba mendapatkan kunci selama waktu ini, dan menyerah jika waktu belum diperoleh.
Karena Trylock tidak selalu memblokir dan menunggu kunci, itu dapat menghindari terjadinya kebuntuan lebih banyak.
lockinterrictly
Lockinterrictly merespons interupsi ketika utas mendapatkan kunci. Jika interupsi terdeteksi, pengecualian interupsi dilemparkan oleh kode lapisan atas. Dalam hal ini, mekanisme keluar disediakan untuk kunci round-robin. Untuk lebih memahami operasi kunci interruptible, sebuah demo ditulis untuk memahaminya.
Paket com.test; import java.util.date; import java.util.concurrent.locks.reentrantlock; kelas publik testlockintertrictly {static reentrantlock lock = baru reentrantlock (); public static void main (string [] args) {thread thread1 = thread baru (runnable baru () {@Override public void run () {coba {doprint ("Thread 1 Get Lock."); do123 (); doprint ("thread 1 end.");} Catch (interruptException e) {doPrint ("Thread."); Thread thread2 = utas baru (runnable baru () {@Override public void run () {try {doprint ("Thread 2 Get Lock."); Do123 (); doprint ("Thread 2 end.");} Catch (interruptedException e) {doprint ("Thread 2 is Interrupped."); thread1.setname ("thread1"); thread2.setname ("thread2"); thread1.start (); coba {thread.sleep (100); // tunggu sebentar untuk membuat thread1 dieksekusi di depan thread2} catch (interruptedException e) {e.printstacktrace (); } thread2.start (); } private static void do123 () melempar interruptedException {lock.lockinterricture (); doprint (thread.currentthread (). getName () + "dikunci."); coba {doprint (thread.currentThread (). getName () + "dosoming1 ...."); Thread.sleep (5000); // tunggu beberapa detik untuk melihat urutan utas doprint (thread.currentthread (). GetName () + "dosoming2 ...."); doprint (thread.currentThread (). getName () + "selesai."); } akhirnya {lock.unlock (); }} private static void doprint (string text) {System.out.println ((new date ()). TolocaleString () + ":" + text); }}Ada dua utas dalam kode di atas. Thread1 dimulai lebih awal dari thread2. Untuk melihat proses kunci, kode yang terkunci tidur selama 5 detik, sehingga Anda dapat merasakan proses kedua utas yang memasuki proses akuisisi kunci. Hasil akhir dari kode di atas adalah sebagai berikut:
2016-9-28 15:12:56: Thread 1 Dapatkan kunci.
2016-9-28 15:12:56: Thread1 terkunci.
2016-9-28 15:12:56: thread1 dosoming1 ....
2016-9-28 15:12:56: Thread 2 Dapatkan kunci.
2016-9-28 15:13:01: thread1 dosoming2 ....
2016-9-28 15:13:01: Thread1 selesai.
2016-9-28 15:13:01: Thread1 diturunkan.
2016-9-28 15:13:01: Thread2 terkunci.
2016-9-28 15:13:01: thread2 dosoming1 ....
2016-9-28 15:13:01: Thread 1 End.
2016-9-28 15:13:06: thread2 dosoming2 ....
2016-9-28 15:13:06: Thread2 selesai.
2016-9-28 15:13:06: Thread2 diturunkan.
2016-9-28 15:13:06: Thread 2 End.
Dapat dilihat bahwa Thread1 memperoleh kunci terlebih dahulu, dan Thread2 juga akan mendapatkan kunci nanti, tetapi Thread1 telah menempatinya saat ini, jadi Thread2 tidak mendapatkan kunci sampai Thread1 melepaskan kunci.
** Kode ini menunjukkan bahwa utas di belakang LockerTericriply untuk memperoleh kunci perlu menunggu kunci sebelumnya dirilis sebelum mendapatkan kunci. ** Tapi belum ada fitur interruptible, jadi beberapa kode ditambahkan ke ini:
thread2.start (); coba {thread.sleep (1000); } catch (InterruptedException e) {e.printstacktrace ();} // interrupt thread2 dalam thread 1 detik2.interrupt ();Setelah Thread2 dimulai, Call Thread2's Metode Interrupt. Oke, jalankan kode terlebih dahulu dan lihat hasilnya:
2016-9-28 15:16:46: Thread 1 Dapatkan kunci.
2016-9-28 15:16:46: Thread1 terkunci.
2016-9-28 15:16:46: thread1 dosoming1 ....
2016-9-28 15:16:46: Thread 2 Get Lock.
2016-9-28 15:16:47: Thread 2 terganggu. <-Menanggapi langsung interupsi utas
2016-9-28 15:16:51: thread1 dosoming2 ....
2016-9-28 15:16:51: Thread1 selesai.
2016-9-28 15:16:51: Thread1 diturunkan.
2016-9-28 15:16:51: Thread 1 End.
Dibandingkan dengan kode sebelumnya, dapat ditemukan bahwa Thread2 sedang menunggu Thread1 untuk melepaskan kunci, tetapi Thread2 sendiri terganggu, dan kode di belakang Thread2 tidak akan terus dieksekusi.
Readwritelock
Seperti namanya, ini adalah kunci baca-tulisan. Skenario aplikasi kunci membaca jenis ini dapat dipahami dengan cara ini. Misalnya, gelombang data sebagian besar disediakan untuk membaca, dan hanya ada sedikit operasi penulisan. Jika kunci mutex digunakan, itu akan menyebabkan kompetisi kunci di antara utas. Jika semua orang dapat membacanya saat membaca, kunci sumber daya setelah ditulis. Perubahan seperti itu menyelesaikan masalah ini dengan baik, memungkinkan operasi baca untuk meningkatkan kinerja baca tanpa mempengaruhi operasi tulis.
Sumber daya dapat diakses oleh banyak pembaca, atau diakses oleh salah satu penulis, dan keduanya tidak dapat dilakukan secara bersamaan.
Ini adalah antarmuka abstrak untuk kunci baca dan tulis, mendefinisikan kunci baca dan kunci tulis.
Antarmuka publik ReadWritelock { /*** Mengembalikan kunci yang digunakan untuk membaca. * * @return kunci yang digunakan untuk membaca */ kunci readlock (); /*** Mengembalikan kunci yang digunakan untuk menulis. * * @return kunci yang digunakan untuk menulis */ lock writelock ();}Ada implementasi ReentrantReadWritelock di JDK, yang merupakan kunci read-write reentrant. ReentrantReadWritelock dapat dibangun menjadi dua jenis: adil atau tidak adil. Jika tidak ditentukan secara eksplisit selama konstruksi, kunci non-fair akan dibuat secara default. Dalam mode kunci non-fair, urutan akses utas tidak pasti, yaitu, dapat dibagi; Ini dapat diturunkan dari penulis ke pembaca, tetapi pembaca tidak dapat ditingkatkan ke penulis.
Jika itu adalah mode kunci yang adil, maka opsi diserahkan ke utas dengan waktu tunggu terpanjang. Jika utas baca mendapatkan kunci dan utas tulis meminta kunci tulis, maka akuisisi kunci baca tidak akan lagi diterima sampai operasi tulis selesai.
Analisis kode sederhana sebenarnya mempertahankan kunci sinkronisasi di ReentrantReadWritelock, tetapi terlihat semantik seperti kunci baca dan kunci tulis. Lihatlah konstruktornya:
publik reentrantreadwritelock (boolean fair) {sync = fair? baru fairsync (): nonfairsync baru (); readerlock = readlock baru (ini); writerLock = new Writelock (this);} // Konstruktor baca kunci readlock yang dilindungi (reentrantreadwritelock lock) {sync = lock.sync;} // Konstruktor writelock yang dilindungi kunci (reentrantReadwritelock lock) {sync = lock.sync;}Anda dapat melihat bahwa objek kunci sinkronisasi dari ReentrantReadWritelock sebenarnya dirujuk saat dibangun. Dan kelas sinkronisasi ini adalah kelas internal ReentrantReadWritelock. Singkatnya, kunci baca/tulis semuanya dilakukan melalui sinkronisasi. Bagaimana cara berkolaborasi dalam hubungan antara keduanya?
// Metode penguncian untuk membaca kunci lock public lock () {sync.acquireshared (1);} // metode penguncian untuk menulis kunci public void lock () {sync.acquire (1);}Perbedaan utama adalah bahwa kunci baca mendapatkan kunci bersama, sedangkan kunci tulis memperoleh kunci eksklusif. Ada satu poin di sini yang dapat disebutkan, yaitu, untuk memastikan ReentrantReadWritelock, baik kunci bersama dan kunci eksklusif harus mendukung jumlah penahanan dan reentrants. Reentrantlock disimpan menggunakan keadaan, dan negara hanya dapat menyimpan satu nilai pembentukan. Agar kompatibel dengan masalah dua kunci, itu dibagi menjadi jumlah utas yang menahan kunci bersama atau jumlah utas yang menahan kunci eksklusif atau jumlah masuk kembali masing -masing.
lainnya
Saya menulis artikel besar yang saya rasa terlalu lama untuk menulis, dan ada beberapa kunci yang lebih berguna:
Countdownlatch
Ini untuk menetapkan konter yang diadakan secara bersamaan. Ketika penelepon memanggil metode menunggu CountdownLatch, itu akan memblokir jika penghitung saat ini tidak 0. Memanggil metode rilis CountdownLatch dapat mengurangi jumlah sampai penelepon yang menelepon menunggu akan dibatalkan.
Semaphone
Semaphore adalah bentuk otorisasi dan lisensi, seperti menyiapkan 100 lisensi, sehingga 100 utas dapat menahan kunci pada saat yang sama, dan jika jumlah ini melebihi, ia akan kembali ke kegagalan.
Terima kasih telah membaca artikel ini, saya harap ini dapat membantu Anda. Terima kasih atas dukungan Anda untuk situs ini!