0. Tentang Mutex
Kunci Mutex yang disebut mengacu pada kunci yang hanya dapat memiliki satu utas pada satu waktu. Sebelum JDK1.5, kami biasanya menggunakan mekanisme yang disinkronkan untuk mengontrol akses beberapa utas ke sumber daya bersama. Sekarang, LOCK menyediakan rentang operasi kunci yang lebih luas daripada mekanisme yang disinkronkan. Perbedaan utama antara mekanisme kunci dan tersinkronisasi:
Mekanisme yang disinkronkan menyediakan akses ke kunci monitor implisit yang terkait dengan masing -masing objek, dan memaksa semua akuisisi dan pelepasan kunci untuk muncul dalam struktur blok. Ketika beberapa kunci diperoleh, mereka harus dilepaskan dalam urutan terbalik. Mekanisme yang disinkronkan melepaskan kunci secara implisit. Selama kode dijalankan oleh utas melebihi ruang lingkup blok pernyataan yang disinkronkan, kunci akan dirilis. Mekanisme kunci harus secara eksplisit memanggil metode buka kunci () dari objek kunci untuk melepaskan kunci, yang menyediakan kemungkinan untuk akuisisi dan pelepasan kunci untuk tidak muncul dalam struktur blok yang sama, dan melepaskan kunci dalam urutan yang lebih bebas.
1. Pengantar Reentrantlock
Reentrantlock adalah kunci mutex reentrant, juga dikenal sebagai "kunci eksklusif".
Seperti namanya, kunci reentrantlock hanya dapat dipegang oleh satu kunci utas pada titik waktu yang sama; Sementara Reentrant berarti bahwa kunci reentrantlock dapat diperoleh beberapa kali dengan satu utas.
Reentrantlock dibagi menjadi "kunci yang adil" dan "kunci tidak adil". Perbedaannya tercermin dalam apakah mekanisme mendapatkan kunci itu adil. "Kunci" adalah untuk melindungi sumber daya yang bersaing dan mencegah banyak utas dari utas operasi pada saat yang sama dan kesalahan. Reentrantlock hanya dapat diperoleh dengan satu utas secara bersamaan (ketika utas memperoleh "kunci", utas lain harus menunggu); Reentraanantlock mengelola semua utas yang memperoleh kunci melalui antrian menunggu FIFO. Di bawah mekanisme "kunci adil", utas antrian untuk memperoleh kunci secara berurutan; Sementara "Non-Fair Lock" akan memperoleh kunci terlepas dari apakah itu di awal antrian atau tidak.
Daftar Fungsi Reentrantlock
// Buat reentrantlock, yang merupakan "kunci tidak adil" secara default. Reentrantlock () // Kebijakan penciptaan adalah reentrantlock pameran. Jika adil benar, itu berarti itu adalah kunci yang adil, dan jika adil salah, itu berarti itu adalah kunci non-fair. Reentrantlock (Boolean Fair) // Permintaan berapa kali utas saat ini telah mempertahankan kunci ini. Int GetHoldCount () // Mengembalikan utas yang saat ini memiliki kunci ini, dan jika kunci ini tidak dimiliki oleh utas apa pun, kembalikan nol. Benang terlindungi GetOwner () // Mengembalikan koleksi yang berisi utas yang mungkin menunggu untuk mendapatkan kunci ini. Koleksi yang Dilindungi <tread> getqueuedThreads () // Mengembalikan perkiraan jumlah utas yang menunggu untuk mendapatkan kunci ini. int getqueuelength () // mengembalikan koleksi yang berisi utas yang mungkin menunggu kondisi tertentu yang terkait dengan kunci ini. Koleksi yang Dilindungi <tread> getWaitingThreads (kondisi kondisi) // Mengembalikan estimasi utas menunggu kondisi yang diberikan yang terkait dengan kunci ini. int getwaitqueelength (kondisi kondisi) // permintaan apakah utas yang diberikan menunggu untuk mendapatkan kunci ini. Boolean HasqueuedThread (Thread Thread) // Permintaan apakah beberapa utas menunggu untuk mendapatkan kunci ini. boolean hasqueuedthreads () // kueri apakah beberapa utas menunggu kondisi tertentu yang terkait dengan kunci ini. boolean haswaiters (kondisi kondisi) // return true jika itu "kunci adil", jika tidak mengembalikan false. boolean isfair () // query apakah utas saat ini mempertahankan kunci ini. boolean isheldbycurrentThread () // query apakah kunci ini dipegang oleh utas apa pun. boolean islocked () // dapatkan kunci. void lock () // Jika utas saat ini tidak terganggu, kunci diperoleh. void lockinterricture () // Mengembalikan instance kondisi yang digunakan untuk digunakan dengan instance kunci ini. Kondisi newcondition () // Hanya memperoleh kunci jika tidak dipegang oleh utas lain selama panggilan. boolean trylock () // Jika kunci tidak dipegang oleh utas lain dalam waktu tunggu yang diberikan dan utas saat ini tidak terganggu, kunci diperoleh. Boolean Trylock (Timeout Long, TimeUnit Unit) // Berusaha melepaskan kunci ini. void unlock ()
2. Contoh Reentrantlock
Dengan membandingkan "Contoh 1" dan "Contoh 2", kita dapat dengan jelas memahami peran kunci dan membuka kunci
2.1 Contoh 1
impor java.util.concurrent.locks.lock; impor java.util.concurrent.locks.reentrantlock; // locktest1.java// class repositori Depot {ukuran int private; // Jumlah aktual kunci kunci pribadi repositori; // Eksklusif Lock Public Depot () {this.size = 0; this.lock = baru reentrantlock (); } public void produce (int val) {lock.lock (); coba {size += val; System.out.printf ("%s Produce (%d) -> size =%d/n", thread.currentThread (). GetName (), val, size); } akhirnya {lock.unlock (); }} konsumsi public void (int val) {lock.lock (); coba {size -= val; System.out.printf ("%S konsumsi (%d) <- ukuran =%d/n", thread.currentThread (). GetName (), val, size); } akhirnya {lock.unlock (); }}}; // Produser Produser {Private Depot Deposit; produsen publik (depot depot) {this.depot = setoran; } // Produk Konsumen: Buat utas baru untuk memproduksi produk ke dalam gudang. public void produce (final int val) {thread baru () {public void run () {deposit.produce (val); } }.awal(); }} // Kelas Konsumen Pelanggan {Private Depot Deposit; Pelanggan publik (depot depot) {this.depot = setoran; } // Produk Konsumen: Buat utas baru untuk mengkonsumsi produk dari gudang. konsumsi public void (final int val) {thread baru () {public void run () {depot.consume (val); } }.awal(); }} kelas publik lockTest1 {public static void main (string [] args) {depot mdepot = new depot (); Produser mPro = produser baru (mDepot); Pelanggan MCUS = Pelanggan Baru (MDEPOT); mpro.produce (60); mpro.produce (120); mcus.consume (90); mcus.consume (150); mpro.produce (110); }} Hasil Menjalankan:
Produk Thread-0 (60)-> size = 60Thread-1 Produce (120)-> size = 180Thread-3 Konsumsi (150) <-size = 30Thread-2 Konsumsi (90) <-size = -60Thread-4 Produce (110)-> Ukuran = 50
Analisis Hasil:
(1) Depot adalah gudang. Barang dapat diproduksi ke gudang melalui produk (), dan barang -barang di gudang dapat dikonsumsi melalui konsumsi (). Akses eksklusif yang saling eksklusif ke gudang dicapai melalui kunci kunci eksklusif: sebelum mengoperasikan barang di gudang (produksi/konsumsi), gudang akan dikunci melalui kunci () terlebih dahulu, dan kemudian dibuka melalui unlock () setelah operasi selesai.
(2) Produser adalah produser. Memanggil fungsi Produce () pada produser dapat membuat utas baru untuk menghasilkan produk di gudang.
(3) Pelanggan adalah kategori konsumen. Memanggil fungsi konsumsi () pada pelanggan dapat membuat produk konsumsi utas baru di gudang.
(4) Di utas utama utama, kami akan membuat MPRO produser baru dan MCU konsumen baru. Mereka masing -masing memproduksi/konsumsi produk menjadi gudang.
Menurut kuantitas produksi/konsumsi di Main, produk akhir yang tersisa di gudang harus 50. Hasil operasi sesuai dengan harapan kami!
Ada dua masalah dengan model ini:
(1) Dalam kenyataannya, kapasitas gudang tidak bisa negatif. Namun, kapasitas gudang dalam model ini bisa negatif, yang bertentangan dengan kenyataan!
(2) Pada kenyataannya, kapasitas gudang terbatas. Namun, benar -benar tidak ada batasan kapasitas dalam model ini!
Kami akan berbicara secara singkat tentang cara menyelesaikan dua masalah ini. Sekarang, mari kita lihat contoh sederhana 2; Dengan membandingkan "Contoh 1" dan "Contoh 2", kita dapat memahami tujuan kunci () dan buka () lebih jelas.
2.2 Contoh 2
impor java.util.concurrent.locks.lock; import java.util.concurrent.locks.reentrantlock; // locktest2.java// class depot {private int size; // Jumlah aktual kunci kunci pribadi repositori; // Eksklusif Lock Public Depot () {this.size = 0; this.lock = baru reentrantlock (); } public void produce (int val) {// lock.lock (); // coba {size += val; System.out.printf ("%S Produce (%d) -> size =%d/n", thread.currentThread (). GetName (), val, size); //} catch (interruptedException e) {//} akhirnya {// lock.unlock (); //}} { -valum {// lock.unlock (); //}} { -valum valum (// lock.unlock (); //}} { -valum valum {//lock ();//{) {//lock. System.out.printf ("%s consume (%d) <- size =%d/n", thread.currentThread (). GetName (), val, size); //} akhirnya {// lock.unlock (); //}}}; // produser produser produser {private depo Depot; Produsen Publik (depos depo) {this.depot = setoran; } // Produk Konsumen: Buat utas baru untuk memproduksi produk ke dalam gudang. public void produce (final int val) {thread baru () {public void run () {deposit.produce (val); } }.awal(); }} // Kelas Konsumen Pelanggan {Private Depot Deposit; Pelanggan publik (depot depot) {this.depot = setoran; } // Produk Konsumen: Buat utas baru untuk mengkonsumsi produk dari gudang. konsumsi public void (final int val) {thread baru () {public void run () {depot.consume (val); } }.awal(); }} kelas publik lockTest2 {public static void main (string [] args) {depot mdepot = new depot (); Produser mPro = produser baru (mDepot); Pelanggan MCUS = Pelanggan Baru (MDEPOT); mpro.produce (60); mpro.produce (120); mcus.consume (90); mcus.consume (150); mpro.produce (110); }} (Satu kali) Hasil:
Produk Thread-0 (60)-> size = -60Thread-4 Produce (110)-> size = 50Thread-2 Konsumsi (90) <-size = -60Thread-1 Produce (120)-> size = -60Thread-3 Konsumsi (150) <-size = -60
Deskripsi Hasil:
"Contoh 2" menghapus kunci kunci berdasarkan "Contoh 1". Dalam Contoh 2, produk akhir yang tersisa di gudang adalah -60, bukan 50 yang kami harapkan. Alasannya adalah kami tidak menerapkan akses mutex ke repositori.
2.3 Contoh 3
Dalam "Contoh 3", kami menggunakan kondisi untuk menyelesaikan dua masalah dalam "Contoh 1": "Kapasitas gudang tidak bisa negatif" dan "kapasitas gudang terbatas".
Solusi untuk masalah ini adalah melalui kondisi. Kondisi perlu digunakan bersama dengan kunci: metode menunggu () dalam kondisi dapat menyebabkan utas memblokir [mirip dengan tunggu ()]; Metode kondisi sinyal () dapat menyebabkan utas bangun untuk [mirip dengan notify ()].
impor java.util.concurrent.locks.lock; impor java.util.concurrent.locks.reentrantlock; impor java.util.concurrent.locks.condition; // locktest3.java// gudang kelas depot {kapasitas int private; // kapasitas gudang ukuran int pribadi; // jumlah aktual gudang kunci kunci pribadi; // KONDISI KUNCI KONDIFIKASI KONDISI PENGOPER FULL; // kondisi produksi kondisi pribadi kosong; // kondisi konsumsi depot publik (kapasitas int) {this.capacity = kapasitas; this.size = 0; this.lock = baru reentrantlock (); this.fullCondtion = lock.newcondition (); this.emptycondition = lock.newcondition (); } public void produce (int val) {lock.lock (); Coba {// kiri berarti "jumlah yang ingin Anda hasilkan" (mungkin terlalu banyak produksi, jadi Anda perlu menghasilkan lebih banyak) int kiri = val; Sementara (kiri> 0) {// Saat inventaris penuh, tunggu "konsumen" untuk mengkonsumsi produk. while (size> = kapasitas) fullCondtion.Await (); // Dapatkan "kuantitas produksi aktual" (mis., Kuantitas baru yang ditambahkan dalam inventaris) // Jika "inventaris" + "kuantitas produksi yang diinginkan"> "kapasitas total", lalu "kenaikan aktual" = "kapasitas total" - "kapasitas saat ini". (Isi gudang saat ini) // sebaliknya "kenaikan aktual" = "jumlah yang ingin Anda hasilkan" int inc = (ukuran+kiri)> kapasitas? (ukuran kapasitas): kiri; Ukuran += Inc; Kiri -= Inc; System.out.printf ("%S Produce (%3D) -> Kiri =%3D, Inc =%3D, ukuran =%3d/n", thread.currentThread (). GetName (), val, kiri, inc, ukuran); // Beri tahu "konsumen" yang dapat Anda konsumsi. kosongCondtion.signal (); }} catch (InterruptedException e) {} akhirnya {lock.unlock (); }} konsumsi public void (int val) {lock.lock (); Coba {// kiri berarti "jumlah konsumsi yang akan dikonsumsi" (mungkin terlalu besar, inventarisnya tidak cukup, jadi Anda perlu mengkonsumsi lebih banyak) int kiri = val; sementara (kiri> 0) {// Saat inventaris 0, tunggu "produser" untuk menghasilkan produk. while (size <= 0) emptleCondtion.Await (); // Dapatkan "kuantitas konsumsi aktual" (mis., Penurunan sebenarnya dalam inventaris) // Jika "inventaris" <"kuantitas yang ingin dikonsumsi pelanggan", lalu "konsumsi aktual" = "inventaris"; // Kalau tidak, "konsumsi aktual" = "kuantitas yang ingin dikonsumsi pelanggan". int dec = (ukuran <kiri)? Ukuran: Kiri; Ukuran -= dec; kiri -= dec; System.out.printf ("%s konsumsi (%3d) <- kiri =%3d, dec =%3d, ukuran =%3d/n", thread.currentThread (). GetName (), val, kiri, dec, ukuran); fullCondtion.signal (); }} catch (InterruptedException e) {} akhirnya {lock.unlock (); }} public String toString () {return "CAPASICY:"+CAPASICY+", ukuran aktual:"+ukuran; }}; // Produser Produser Produser {Private Depot Deposit; Produsen Publik (depos depo) {this.depot = setoran; } // Produk Konsumen: Buat utas baru untuk memproduksi produk ke dalam gudang. public void produce (final int val) {thread baru () {public void run () {deposit.produce (val); } }.awal(); }} // Kelas Konsumen Pelanggan {Private Depot Deposit; Pelanggan publik (depot depot) {this.depot = setoran; } // Produk Konsumen: Buat utas baru untuk mengkonsumsi produk dari gudang. konsumsi public void (final int val) {thread baru () {public void run () {depot.consume (val); } }.awal(); }} public class lockTest3 {public static void main (string [] args) {depot mdepot = depot baru (100); Produser mPro = produser baru (mDepot); Pelanggan MCUS = Pelanggan Baru (MDEPOT); mpro.produce (60); mpro.produce (120); mcus.consume (90); mcus.consume (150); mpro.produce (110); }} (Satu kali) Hasil:
Thread-0 produce( 60) --> left= 0, inc= 60, size= 60Thread-1 produce(120) --> left= 80, inc= 40, size=100Thread-2 consumption( 90) <-- left= 0, dec= 90, size= 10Thread-3 consumption(150) <-- left=140, dec= 10, size= 0Thread-4 produce(110) --> left= 10, inc=100, size=100Thread-3 Konsumsi (150) <-kiri = 40, dec = 100, ukuran = 0Thread-4 Produce (110)-> kiri = 0, inc = 10, ukuran = 10Thread-3 Konsumsi (150) <-kiri = 30, dec = 10, ukuran = 0thread-1 produk (120)-> Kiri = 0, Inc = 80, Ukuran = 80Thread-3 Consume (150) <11)-> Kiri = 0, Inc = 80, Ukuran = 80Thread-3 Consume (150) <120)-> Kiri = 0, Inc = 80, Ukuran = 80thread-3 Consume (150)