Sinkronisasi dan Reentrantlock
Dalam pemrograman multi-threaded, kami menggunakan kunci ketika kode perlu disinkronkan. Java memberi kami dua metode sinkronisasi: kunci bawaan (disinkronkan) dan kunci eksplisit (reentrantlock). Kunci eksplisit diperkenalkan oleh JDK1.5. Apa persamaan dan perbedaan antara kedua kunci ini? Apakah ini hanya pilihan atau ada alasan lain? Artikel ini akan mencari tahu untuk Anda.
// Contoh Penggunaan Kata Kunci Sinkronisasi Public Void Void Add (int t) {// Metode disinkronkan this.v += t;} public static static void sub (int t) {// nilai metode statis yang disinkronkan -= t;} public int decrementandget () {disinkronisasi (OBJ) {/// T;} public int decrementandget () {Synchronized (OBJ) {/// t; }}Ini semua tentang menggunakan kunci bawaan, Anda telah mempelajarinya.
Kunci bawaan sangat nyaman untuk digunakan, dan tidak memerlukan akuisisi dan pelepasan eksplisit. Objek apa pun dapat digunakan sebagai kunci bawaan. Menggunakan kunci bawaan dapat menyelesaikan sebagian besar skenario sinkronisasi. "Objek apa pun dapat digunakan sebagai kunci bawaan" juga berarti bahwa di mana kata kunci yang disinkronkan muncul, ada objek yang terkait dengannya. Secara khusus:
Ketika disinkronkan bertindak pada metode normal, objek kunci adalah ini;
Ketika disinkronkan bertindak pada metode statis, objek kunci adalah objek kelas dari kelas saat ini;
Saat disinkronkan bertindak pada blok kode, objek kunci adalah OBJ ini dalam sinkronisasi (OBJ).
Kunci eksplisit
Kunci bawaan sangat mudah digunakan, mengapa Anda membutuhkan kunci ekstra eksplisit? Karena beberapa hal tidak dapat dilakukan dengan kunci bawaan, seperti:
Kami ingin menambahkan waktu tunggu ke kunci, dan menyerah sebelum batas waktu telah diperoleh, sehingga kami tidak akan menunggu tanpa batas;
Kami ingin memperoleh kunci dengan cara yang terputus, sehingga utas eksternal mengirimi kami sinyal interupsi dan dapat membangkitkan utas menunggu kunci;
Kami ingin mempertahankan beberapa antrian menunggu untuk kunci, seperti antrian produsen dan antrian konsumen, sambil meningkatkan efisiensi kunci.
Kunci eksplisit (reentrantlocks) secara resmi dilahirkan untuk menyelesaikan kebutuhan fleksibel ini. Reentrantlock secara harfiah berarti kunci masuk kembali, dan Reentrant berarti bahwa utas dapat meminta kunci yang sama beberapa kali pada saat yang sama tanpa menyebabkannya menemui jalan buntu dengan sendirinya. Inilah perbedaan antara kunci bawaan dan kunci eksplisit:
Waktu yang dapat diatur: renentrantlock.trylock (waktu lama, unit TimeUnit) menyediakan cara untuk mengakhiri waktu tunggu. Jika utas tidak mendapatkan kunci dalam waktu yang ditentukan, metode akan mengembalikan false dan mengakhiri utas menunggu.
Interruptible: Anda pasti telah melihat ExterruptedException. Banyak metode multi-threaded akan melempar pengecualian ini. Pengecualian ini bukan beban yang disebabkan oleh cacat, tetapi suatu keharusan, atau hal yang baik. Interruptity memberi kita cara untuk mengakhiri utas lebih awal (daripada harus menunggu sampai eksekusi utas berakhir), yang sangat berguna untuk tugas yang memakan waktu. Untuk kunci bawaan, utas akan menunggu kunci bawaan jika tidak bisa mendapatkannya. Tidak ada cara lain untuk mengakhiri penantian kecuali untuk mendapatkan kunci. Renentrantlock.lockinterruptible () memberi kita cara untuk mengakhiri penantian dengan interupsi.
Kondisi Antrian: Setelah utas memperoleh kunci, ia dapat memasuki keadaan menunggu karena menunggu kondisi tertentu terjadi (kunci bawaan melewati metode objek.Wait (), dan kunci eksplisit melewati kondisi.AWAIT (Metode). Utas yang memasuki status menunggu akan menggantung dan secara otomatis melepaskan kunci, dan utas ini akan dimasukkan ke dalam antrian kondisi. Sinkronisasi hanya memiliki satu antrian bersyarat, sedangkan reentrantlock dapat memiliki beberapa antrian bersyarat. Apa manfaat dari beberapa antrian? Bacalah.
Predikat bersyarat: Setelah utas mendapatkan kunci, kadang -kadang perlu menunggu kondisi tertentu untuk dipenuhi sebelum dapat melakukan sesuatu. Misalnya, produsen perlu menunggu sampai cache tidak puas sebelum dapat memasukkan pesan ke dalam antrian, sementara konsumen perlu menunggu sampai cache tidak kosong sebelum dapat mengambil pesan dari antrian. Kondisi ini disebut predikat bersyarat. Utas perlu memperoleh kunci terlebih dahulu, dan kemudian menentukan apakah predikat bersyarat terpenuhi. Jika tidak puas, itu tidak akan dieksekusi ke bawah. Utas yang sesuai akan melepaskan eksekusi dengan benar dan secara otomatis melepaskan kunci. Benang yang berbeda menggunakan kunci yang sama mungkin memiliki predikat bersyarat yang berbeda. Jika hanya ada satu antrian bersyarat, ketika predikat bersyarat terpenuhi, tidak mungkin untuk menentukan utas mana dalam antrian bersyarat untuk bangun; tetapi jika setiap predikat bersyarat memiliki antrian bersyarat yang terpisah, ketika suatu kondisional terpenuhi, kita tahu bahwa utas pada antrian yang sesuai harus dibangunkan (kunci bawaan dibangunkan melalui objek. Notifikasi () atau objek. Metode NotifyAll (), dan Metode Eksplisit dibangunkan melalui kondisi. Ini adalah manfaat dari beberapa antrian bersyarat.
Saat menggunakan kunci bawaan, objek itu sendiri merupakan kunci dan antrian bersyarat; Saat menggunakan kunci eksplisit, objek renentrantlock adalah kunci, dan antrian bersyarat diperoleh melalui metode renentrantlock.newcondition (). Antrian bersyarat berganda dapat diperoleh dengan memanggil metode ini beberapa kali.
Contoh khas menggunakan kunci eksplisit adalah sebagai berikut:
// Contoh menggunakan kunci eksplisit reentrantlock lock = baru reentrantlock (); // memperoleh kunci, ini adalah penggunaan yang sesuai dengan kata kunci yang disinkronkan. lock.lock (); coba {// kode Anda} akhirnya {lock.unlock ();} // Anda bisa waktu, dan menyerahkan kunci jika Anda mendapatkan kunci setelah waktu yang ditentukan terlampaui. coba {lock.trylock (10, timeunit.seconds); coba {// kode Anda} akhirnya {lock.unlock (); }} catch (InterruptedException E1) {// Penanganan Pengecualian} // Anda dapat mengganggu, dan utas utas dapat terganggu sambil menunggu kunci untuk diperoleh cobalah {lock.lockinterricture (); coba {// kode Anda} akhirnya {lock.unlock (); }} Catch (InterruptedException E) {// Penanganan Pengecualian} // Beberapa antrian menunggu, silakan merujuk ke [arrayblockingqueue] (https://github.com/carpenterlee/jcrecopes/blob/master/markdown/arrayblockingqueue.md untuk)/** KONDIPARI/PRICLODDDOWDOWDOWDOWDOWDOWDODE-ROMECKDYUE.MD)/** lock.newcondition ();/** Kondisi untuk menunggu*/kondisi akhir pribadi khas = lock.newcondition ();Perhatikan bahwa kode di atas menempatkan buka kunci () di blok akhirnya, yang diperlukan. Kunci eksplisit tidak akan secara otomatis dirilis seperti kunci bawaan. Saat menggunakan kunci eksplisit, itu harus dirilis secara manual di blok akhirnya. Jika kunci tidak dilepaskan karena pengecualian setelah mendapatkan kunci, maka kunci tidak akan pernah dilepaskan! Letakkan buka kunci () di blok akhirnya untuk memastikan bahwa itu dapat dirilis secara normal tidak peduli apa yang terjadi.
sebagai kesimpulan
Kunci bawaan dapat menyelesaikan sebagian besar skenario yang membutuhkan sinkronisasi. Hanya ketika fleksibilitas tambahan diperlukan, kunci eksplisit perlu dipertimbangkan, seperti waktu, interupsi, dan beberapa antrian menunggu.
Meskipun kunci eksplisit fleksibel, mereka membutuhkan aplikasi dan rilis eksplisit, dan rilis harus ditempatkan di blok akhirnya, jika tidak kunci mungkin tidak pernah dilepaskan karena pengecualian! Ini adalah kerugian yang paling jelas dari penguncian eksplisit.
Singkatnya, saat menyinkronkan, harap beri prioritas untuk lebih aman dan lebih mudah untuk menggunakan kunci implisit.