Java5 sudah berisi kunci baca dan tulis di paket java.util.concurrent. Namun demikian, kita harus memahami prinsip -prinsip di balik implementasinya.
Implementasi Java dari kunci baca/tulis
Pertama -tama mari kita berikan gambaran tentang kondisi untuk membaca dan menulis akses ke sumber daya:
Membaca No Thread sedang melakukan operasi tulis, dan tidak ada utas yang meminta operasi tulis.
Tidak ada utas yang melakukan operasi baca dan tulis.
Jika utas ingin membaca sumber daya, selama tidak ada utas yang menulis ke sumber daya dan tidak ada permintaan utas yang ditulis ke sumber daya. Kami berasumsi bahwa permintaan operasi penulisan lebih penting daripada permintaan operasi baca, jadi kami harus meningkatkan prioritas permintaan penulisan. Selain itu, jika operasi baca sering terjadi dan kami tidak meningkatkan prioritas operasi penulisan, maka "kelaparan" akan terjadi. Utas yang meminta operasi tulis akan diblokir sampai semua utas baca tidak dikunci dari ReadWritelock. Jika izin operasi baca dari utas baru selalu dijamin, utas yang menunggu operasi tulis akan terus memblokir, dan hasilnya akan menjadi "kelaparan". Oleh karena itu, operasi yang dibaca hanya dapat dijamin untuk dilanjutkan ketika tidak ada utas yang mengunci ReadWritelock untuk operasi penulisan, dan tidak ada utas yang meminta kunci untuk siap untuk operasi tulis.
Ketika utas lain tidak membaca atau menulis operasi pada sumber daya bersama, utas dapat memperoleh kunci tulis untuk sumber daya bersama dan kemudian menulis operasi pada sumber daya bersama. Tidak peduli berapa banyak utas permintaan yang ditulis dan dalam urutan apa, kecuali jika Anda ingin memastikan keadilan dari permintaan kunci tulis.
Menurut deskripsi di atas, kunci baca/tulis hanya diimplementasikan, dan kodenya adalah sebagai berikut
kelas publik readwritelock {pembaca int private = 0; private int writers = 0; private int writerequests = 0; lockRead void yang disinkronkan publik () melempar interruptedException {while (penulis> 0 || writerequests> 0) {tunggu (); } pembaca ++; } public disinkronkan void uncockRead () {pembaca--; notifyall (); } public yang disinkronkan void lockwrite () melempar InterruptedException {writerequests ++; while (pembaca> 0 || penulis> 0) {tunggu (); } WriteRequests--; penulis ++; } public disinkronkan void unlockwrite () melempar InterruptedException {Writers--; notifyall (); }}Di kelas ReadWritelock, baca Lock dan Write Lock masing -masing memiliki metode untuk memperoleh dan melepaskan kunci.
Implementasi kunci baca ada di lockread (). Selama tidak ada utas yang memiliki kunci tulis (penulis == 0) dan tidak ada utas yang meminta kunci tulis (writerequests == 0), semua utas yang ingin mendapatkan kunci baca dapat berhasil diperoleh.
Implementasi kunci tulis ada di lockwrite (). Ketika utas ingin mendapatkan kunci tulis, pertama -tama akan menambahkan 1 ke nomor permintaan kunci tulis (writerequests ++), dan kemudian menentukan apakah itu benar -benar dapat memperoleh kunci tulis. Ketika tidak ada utas yang memegang kunci baca (pembaca == 0) dan tidak ada utas yang memegang kunci tulis (penulis == 0) Anda dapat memperoleh kunci tulis. Tidak masalah berapa banyak utas yang meminta untuk menulis kunci.
Perlu dicatat bahwa di kedua UnlockRead, UnlockWrite, metode NotifyAll dipanggil alih -alih memberi tahu. Untuk menjelaskan alasan ini, kita dapat membayangkan situasi berikut:
Jika utas menunggu untuk mendapatkan kunci baca, dan utas menunggu untuk mendapatkan kunci tulis. Jika salah satu utas yang menunggu kunci baca dibangunkan oleh metode notify, tetapi karena masih ada utas yang meminta kunci tulis (Writerequests> 0), utas yang terbangun akan memasuki status pemblokiran lagi. Namun, tidak ada utas yang menunggu kunci tulis dibangunkan, seolah -olah tidak ada yang terjadi (catatan penerjemah: kehilangan sinyal). Jika Anda menggunakan metode NotifyAll, semua utas akan dibangunkan dan kemudian tentukan apakah mereka dapat memperoleh kunci yang mereka minta.
Ada juga satu manfaat untuk menggunakan NotifyAll. Jika beberapa utas baca sedang menunggu kunci baca dan tidak ada utas yang menunggu kunci tulis, setelah memanggil unlockwrite (), semua utas menunggu kunci baca dapat segera berhasil memperoleh kunci baca - bukan hanya satu per satu.
Masuk kembali dari kunci baca/tulis
Kunci baca/tulis yang diimplementasikan di atas tidak masuk kembali dan akan diblokir ketika utas yang sudah menahan kunci tulis meminta kunci tulis lagi. Alasannya adalah bahwa sudah ada utas menulis - itu sendiri. Juga, pertimbangkan contoh berikut:
Untuk membuat readwritelock kembali, beberapa perbaikan perlu dilakukan. Berikut ini akan menangani masuk kembali dari kunci baca dan masuk kembali dari Write Lock masing -masing.
Baca Kunci Pemasangan kembali
Untuk membuat kunci baca readwritelock reentrant, pertama -tama kita harus menetapkan aturan untuk reentrant kunci baca:
Untuk memastikan bahwa kunci baca di utas adalah masuk kembali, baik memenuhi persyaratan untuk mendapatkan kunci baca (tidak ada permintaan tulis atau tulis) atau sudah menahan kunci baca (terlepas dari apakah ada permintaan tulis atau tidak). Untuk menentukan apakah utas telah memegang kunci baca, peta dapat digunakan untuk menyimpan utas yang telah memegang kunci baca dan berapa kali utas yang sesuai memperoleh kunci baca. Ketika diperlukan untuk menentukan apakah utas dapat memperoleh kunci baca, data yang disimpan dalam peta digunakan untuk membuat penilaian. Berikut ini adalah kode yang dimodifikasi dari metode Lockread dan Unlockread:
kelas publik readwritelock {private Map <thread, integer> bacaanThreads = new HashMap <thread, integer> (); private int writers = 0; private int writerequests = 0; public disinkronkan void lockRead () melempar interruptedException {thread callesThread = thread.currentThread (); while (! canGrantReadAccess (callingThread)) {tunggu (); } ReadingThreads.put (CallingThread, (GetAccessCount (CallingThread) + 1)); } public disinkronkan void uncockRead () {thread callesThread = thread.currentThread (); int accessCount = getAccessCount (CallingThread); if (accessCount == 1) {readingThreads.remove (CallingThread); } else {readingThreads.put (CallingThread, (AccessCount -1)); } notifyall (); } private boolean canGrantReadAccess (thread callingthread) {if (writers> 0) return false; if (isReader (CallingThread) Return true; if (writeRequests> 0) return false; return true;} private int getReadAccessCount (thread callingThread) {integer accessCount = readingThreads.get (callingThread); if (accessCount == null) return 0; return accessCount.IntValue (); if AccessCount == null) kembali 0; return acustCount.IntVale (); if accessCount == null) kembali 0; return acustCount.IntValue (); af accessCount = ReadingThreads.get (CallingThread)! = NULL;}}Dalam kode, kita dapat melihat bahwa masuk kembali kunci baca hanya diizinkan jika tidak ada utas yang memiliki kunci tulis. Selain itu, kunci baca reentrant memiliki prioritas yang lebih tinggi daripada kunci menulis.
Tulis kembali kunci
Write Lock Reentry diizinkan hanya jika utas sudah memegang kunci tulis (mendapatkan kembali kunci tulis). Berikut ini adalah kode yang dimodifikasi dari Metode Lockwrite dan UnlockWrite.
kelas publik readwritelock {private Map <thread, integer> bacaanThreads = new HashMap <thread, integer> (); private int writeaccesses = 0; private int writerequests = 0; private thread writingthread = null; lockwrite yang disinkronkan publik () melempar interruptedException {writerequests ++; Thread callesThread = thread.currentThread (); while (! cangrantwriteAccess (callingThread)) {tunggu (); } WriteRequests--; writeaccesses ++; WritingThread = CallingThread; } public disinkronkan void unlockwrite () melempar interruptedException {writeaccesses--; if (writeaccesses == 0) {writingthread = null; } notifyall (); } private boolean cangrantwriteAccess (thread callingthread) {if (hasreaders ()) return false; if (writingthread == null) return true; if (! isWriter (CallingThread)) Return False; Kembali Benar; } private boolean hasreaders () {return readingThreads.size ()> 0; } private boolean isWriter (thread callingthread) {return writingthread == calleThread; }}Perhatikan cara menghadapinya ketika menentukan apakah utas saat ini dapat memperoleh kunci tulis.
Baca Upgrade Kunci untuk Menulis Kunci
Terkadang, kami menginginkan utas yang memiliki kunci baca untuk mendapatkan kunci tulis. Untuk mengizinkan operasi seperti itu, utas ini harus menjadi satu -satunya utas dengan kunci baca. Writelock () perlu membuat beberapa perubahan untuk mencapai tujuan ini:
kelas publik readwritelock {private Map <thread, integer> bacaanThreads = new HashMap <thread, integer> (); private int writeaccesses = 0; private int writerequests = 0; private thread writingthread = null; lockwrite yang disinkronkan publik () melempar interruptedException {writerequests ++; Thread callesThread = thread.currentThread (); while (! cangrantwriteAccess (callingThread)) {tunggu (); } WriteRequests--; writeaccesses ++; WritingThread = CallingThread; } public disinkronkan void unlockwrite () melempar interruptedException {writeaccesses--; if (writeaccesses == 0) {writingthread = null; } notifyall (); } private boolean cangrantwriteAccess (thread callingthread) {if (isOnlyreader (callingThread)) return true; if (hasreaders ()) mengembalikan false; if (writingthread == null) return true; if (! isWriter (CallingThread)) Return False; Kembali Benar; } private boolean hasreaders () {return readingThreads.size ()> 0; } private boolean isWriter (thread callingthread) {return writingthread == calleThread; } private boolean isOnlyReader (thread thread) {return readers == 1 && readingThreads.get (callingThread)! = null; }}Sekarang kelas ReadWritelock dapat ditingkatkan dari kunci baca ke kunci tulis.
Tulis kunci untuk membaca kunci
Terkadang utas yang memiliki kunci tulis juga ingin mendapatkan kunci baca. Jika utas memiliki kunci tulis, maka utas lainnya tidak dapat memiliki kunci baca atau kunci tulis. Oleh karena itu, tidak ada bahaya untuk utas yang memiliki kunci tulis dan kemudian mendapatkan kunci baca. Kita hanya perlu membuat modifikasi sederhana pada metode CanGrantReadAccess di atas:
kelas publik readwritelock {private boolean canGrantReadAccess (thread callingthread) {if (isWriter (callingThread)) return true; if (writingthread! = null) return false; if (isReader (CallingThread) Return true; if (writeRequests> 0) Return false; return true;}}Implementasi lengkap Readwritelock Reentrant
Di bawah ini adalah implementasi ReadWritelock lengkap. Untuk memfasilitasi pembacaan dan pemahaman kode, kode di atas telah direfaktor. Kode refactored adalah sebagai berikut.
kelas publik readwritelock {private Map <thread, integer> bacaanThreads = new HashMap <thread, integer> (); private int writeaccesses = 0; private int writerequests = 0; private thread writingthread = null; public disinkronkan void lockRead () melempar interruptedException {thread callesThread = thread.currentThread (); while (! canGrantReadAccess (callingThread)) {tunggu (); } ReadingThreads.put (CallingThread, (GetReadAccessCount (CallingThread) + 1)); } private boolean canGrantReadAccess (thread callingthread) {if (isWriter (callingThread)) return true; if (haswriter ()) mengembalikan false; if (isReader (CallingThread)) kembali true; if (haswriterquests ()) mengembalikan false; Kembali Benar; } public disinkronkan void uncockRead () {thread callesThread = thread.currentThread (); if (! isReader (CallingThread)) {lempar IllegalMonitorStateException baru ("Benang Panggilan tidak" + "Pegang kunci baca di ReadWritelock ini"); } int accessCount = getReAdAccessCount (CallingThread); if (accessCount == 1) {readingThreads.remove (CallingThread); } else {readingThreads.put (CallingThread, (AccessCount -1)); } notifyall (); } public yang disinkronkan void lockwrite () melempar InterruptedException {writerequests ++; Thread callesThread = thread.currentThread (); while (! cangrantwriteAccess (callingThread)) {tunggu (); } WriteRequests--; writeaccesses ++; WritingThread = CallingThread; } public synchronized void unlockWrite() throws InterruptedException{ if(!isWriter(Thread.currentThread()){ throw new IllegalMonitorStateException( "Calling Thread does not" + " hold the write lock on this ReadWriteLock"); } writeAccesses--; if(writeAccesses == 0){ writingThread = null; } notifyAll(); } Private Boolean CangrantwriteAccess (Thread CallingThread) {if (IsOnlyReader (CallingThread)) Return True; ReadingThreads.get (CallingThread); readingThreads.size () == 1 && readingThreads.get (CallingThread)! = Null; } private boolean haswriter () {return writeThread! = null; } private boolean isWriter (thread callingthread) {return writingthread == calleThread; } private boolean haswriterquests () {return this.writerquests> 0; }}Hubungi buka kunci () di akhirnya
Saat menggunakan ReadWritelock untuk melindungi zona kritis, jika zona kritis dapat melemparkan pengecualian, penting untuk memanggil readunlock () dan writeunlock () di blok akhirnya. Ini dilakukan untuk memastikan bahwa ReadWritelock dapat berhasil dibuka, dan utas lainnya dapat meminta kunci. Inilah contohnya:
lock.lockwrite (); coba {// lakukan kode bagian kritis, yang mungkin melempar pengecualian} akhirnya {lock.unlockwrite ();}Struktur kode di atas dapat memastikan bahwa ReadWritelock juga akan dirilis ketika pengecualian dilemparkan ke area kritis. Jika metode unlockwrite tidak dipanggil di blok akhirnya, ketika pengecualian dilemparkan ke bagian kritis, ReadWritelock akan tetap dalam keadaan kunci tulis, yang akan menyebabkan semua utas memanggil lockread () atau lockwrite () untuk diblokir. Satu-satunya faktor yang dapat melambai kembali ReadWritelock mungkin adalah bahwa ReadWritelock adalah masuk kembali. Ketika pengecualian dilemparkan, utas dapat berhasil memperoleh kunci, lalu jalankan bagian kritis dan hubungi unlockwrite () lagi, yang akan merilis ReadWritelock lagi. Tetapi bagaimana jika utas tidak lagi memperoleh kunci? Oleh karena itu, memanggil UnlockWrite di akhirnya sangat penting untuk menulis kode yang kuat.
Di atas adalah kompilasi dari informasi multi-threaded Java. Kami akan terus menambahkan informasi yang relevan di masa mendatang. Terima kasih atas dukungan Anda untuk situs web ini!