Jika satu Googles untuk "perbedaan antara notify()
dan notifyAll()
" maka banyak penjelasan akan muncul (meninggalkan paragraf javadoc). Semuanya bermuara pada jumlah utas menunggu yang terbangun: satu masuk notify()
dan semua masuk notifyAll()
.
Namun (jika saya mengerti perbedaan antara metode ini dengan benar), hanya satu utas yang selalu dipilih untuk akuisisi monitor selanjutnya; dalam kasus pertama yang dipilih oleh VM, dalam kasus kedua yang dipilih oleh penjadwal thread sistem. Prosedur pemilihan yang tepat untuk keduanya (dalam kasus umum) tidak diketahui oleh programmer.
Apa perbedaan yang bermanfaat antara notify () dan notifyAll () ? Apakah saya melewatkan sesuatu?
java
multithreading
Sergey Mikhanov
sumber
sumber
synchronized
, kecuali untuk kepastian tertentu. , kasus penggunaan agak jarang.notifyAll()
kasus ini, _ utas lainnya setelah yang pertama tetap terjaga dan akan mendapatkan monitor, satu per satu. Dalamnotify
kasus ini, tidak ada utas lainnya yang terbangun. Jadi secara fungsional mereka sangat berbeda!blocked
dan tidakwaiting
. Ketikablocked
pelakunya ditangguhkan sementara sampai utas lain berada di dalamsync
blok.Jawaban:
Itu tidak benar.
o.notifyAll()
membangunkan semua utas yang diblokir dalamo.wait()
panggilan. Utas hanya diizinkan untuk kembali dario.wait()
satu per satu, tetapi masing-masing akan mendapat giliran.Sederhananya, itu tergantung pada mengapa utas Anda menunggu untuk diberitahu. Apakah Anda ingin memberi tahu salah satu utas menunggu bahwa sesuatu terjadi, atau Anda ingin menceritakan semuanya pada saat yang sama?
Dalam beberapa kasus, semua utas tunggu dapat mengambil tindakan bermanfaat setelah menunggu selesai. Contohnya adalah serangkaian utas menunggu tugas tertentu selesai; setelah tugas selesai, semua utas menunggu dapat melanjutkan bisnis mereka. Dalam kasus seperti itu, Anda akan menggunakan notifyAll () untuk membangunkan semua utas secara bersamaan.
Kasus lain, misalnya penguncian yang saling eksklusif, hanya satu dari utas tunggu yang dapat melakukan sesuatu yang berguna setelah diberi tahu (dalam hal ini memperoleh kunci). Dalam kasus seperti itu, Anda lebih suka menggunakan notify () . Diimplementasikan dengan benar, Anda dapat menggunakan notifyAll () dalam situasi ini juga, tetapi Anda tidak perlu membangunkan utas yang tidak dapat melakukan apa pun.
Dalam banyak kasus, kode untuk menunggu suatu kondisi akan ditulis sebagai loop:
Dengan begitu, jika sebuah
o.notifyAll()
panggilan membangunkan lebih dari satu utas menunggu, dan yang pertama kembali dario.wait()
make meninggalkan kondisi dalam keadaan salah, maka utas lainnya yang terbangun akan kembali ke menunggu.sumber
Jelas,
notify
bangun (ada) satu utas di set tunggu,notifyAll
bangun semua utas di set tunggu. Diskusi berikut harus menjernihkan keraguan.notifyAll
harus digunakan sebagian besar waktu. Jika Anda tidak yakin mana yang akan digunakan, maka gunakan.notifyAll
Silakan lihat penjelasan yang berikut.Baca dengan sangat hati-hati dan mengerti. Silakan kirim saya email jika Anda memiliki pertanyaan.
Lihatlah produsen / konsumen (asumsi adalah kelas ProduserConsumer dengan dua metode). ITU RUSAK (karena menggunakan
notify
) - ya itu MUNGKIN bekerja - bahkan sebagian besar waktu, tetapi juga dapat menyebabkan kebuntuan - kita akan melihat mengapa:PERTAMA,
Mengapa kita perlu loop sementara di sekitar menunggu?
Kami membutuhkan
while
loop jika kami mendapatkan situasi ini:Konsumen 1 (C1) memasuki blok yang disinkronkan dan buffer kosong, sehingga C1 dimasukkan ke dalam set tunggu (melalui
wait
panggilan). Konsumen 2 (C2) akan memasuki metode yang disinkronkan (pada titik Y di atas), tetapi Produser P1 menempatkan objek di buffer, dan kemudian memanggilnotify
. Satu-satunya utas menunggu adalah C1, sehingga dibangunkan dan sekarang mencoba untuk mendapatkan kembali kunci objek pada titik X (di atas).Sekarang C1 dan C2 berusaha mendapatkan kunci sinkronisasi. Salah satunya (tanpa syarat) dipilih dan memasuki metode, yang lain diblokir (tidak menunggu - tetapi diblokir, mencoba untuk mendapatkan kunci pada metode). Katakanlah C2 mendapatkan kunci terlebih dahulu. C1 masih memblokir (berusaha mendapatkan kunci di X). C2 menyelesaikan metode dan melepaskan kunci. Sekarang, C1 mendapatkan kunci. Coba tebak, beruntung kita memiliki
while
loop, karena, C1 melakukan loop check (guard) dan dicegah untuk menghapus elemen yang tidak ada dari buffer (C2 sudah mendapatkannya!). Jika kita tidak memilikiwhile
, kita akan mendapatkanIndexArrayOutOfBoundsException
ketika C1 mencoba untuk menghapus elemen pertama dari buffer!SEKARANG,
Oke, sekarang mengapa kita perlu memberi tahu SEMUA?
Dalam contoh produsen / konsumen di atas, sepertinya kita bisa lolos
notify
. Kelihatannya seperti ini, karena kita dapat membuktikan bahwa penjaga di loop menunggu untuk produsen dan konsumen saling eksklusif. Artinya, sepertinya kita tidak dapat memiliki utas menunggu dalamput
metode maupunget
metode, karena, agar itu benar, maka yang berikut ini harus benar:buf.size() == 0 AND buf.size() == MAX_SIZE
(anggap MAX_SIZE bukan 0)NAMUN, ini tidak cukup baik, kami PERLU untuk menggunakannya
notifyAll
. Mari kita lihat mengapa ...Asumsikan kita memiliki buffer ukuran 1 (untuk membuat contoh mudah diikuti). Langkah-langkah berikut membawa kita ke jalan buntu. Perhatikan bahwa KAPAN SAJA utas dibangunkan dengan memberi tahu, utas dapat dipilih secara non-deterministik oleh JVM - yaitu utas tunggu mana pun dapat dibangunkan. Perhatikan juga bahwa ketika banyak utas menghalangi saat masuk ke metode (yaitu mencoba memperoleh kunci), urutan akuisisi dapat menjadi non-deterministik. Ingat juga bahwa utas hanya dapat berada dalam salah satu metode pada satu waktu - metode yang disinkronkan hanya memungkinkan satu utas untuk mengeksekusi (yaitu memegang kunci) metode apa saja (disinkronkan) di kelas. Jika urutan peristiwa berikut terjadi - hasil kebuntuan:
LANGKAH 1:
- P1 menempatkan 1 char ke buffer
LANGKAH 2:
- Upaya P2
put
- memeriksa lingkaran tunggu - sudah menjadi tanda tungguLANGKAH 3:
- Upaya P3
put
- memeriksa lingkaran tunggu - sudah menjadi tanda tungguLANGKAH 4:
- C1 berupaya mendapatkan 1 karakter
- C2 mencoba mendapatkan 1 karakter - blok saat masuk ke
get
metode- C3 berupaya mendapatkan 1 karakter - blok saat masuk ke
get
metodeLANGKAH 5:
- C1 mengeksekusi
get
metode - mendapatkan char, panggilannotify
, metode keluar-
notify
Bangun P2- TETAPI, C2 memasukkan metode sebelum P2 dapat (P2 harus mendapatkan kembali kunci), jadi blok P2 pada saat masuk ke
put
metode- C2 memeriksa loop menunggu, tidak ada lagi karakter dalam buffer, jadi tunggu
- C3 memasuki metode setelah C2, tetapi sebelum P2, memeriksa loop menunggu, tidak ada lagi karakter dalam buffer, jadi tunggu
LANGKAH 6:
- SEKARANG: ada P3, C2, dan C3 menunggu!
- Akhirnya P2 mendapatkan kunci, menempatkan char di buffer, panggilan notify, keluar dari metode
LANGKAH 7:
- Pemberitahuan P2 membangunkan P3 (ingat utas apa pun dapat dibangunkan)
- P3 memeriksa kondisi loop menunggu, sudah ada arang di buffer, jadi tunggu.
- TIDAK ADA LEBIH BANYAK BENANG UNTUK PEMBERITAHUAN dan TIGA THREAD YANG DITANGGUHKAN!
SOLUSI: Ganti
notify
dengannotifyAll
kode produsen / konsumen (di atas).sumber
notify
penyebab P3 (utas yang dipilih dalam contoh ini) untuk melanjutkan dari titik itu sedang menunggu (yaitu dalamwhile
loop). Ada contoh lain yang tidak menyebabkan kebuntuan, namun, dalam hal ini penggunaannotify
tidak menjamin kode bebas dari kebuntuan. PenggunaannotifyAll
tidak.Perbedaan yang berguna:
Gunakan beri tahu () jika semua utas menunggu Anda dapat dipertukarkan (urutan mereka bangun tidak masalah), atau jika Anda hanya memiliki satu utas tunggu. Contoh umum adalah kumpulan utas yang digunakan untuk menjalankan pekerjaan dari antrian - ketika suatu pekerjaan ditambahkan, salah satu utas diberitahu untuk bangun, melaksanakan pekerjaan berikutnya dan kembali tidur.
Gunakan notifyAll () untuk kasus lain di mana utas tunggu mungkin memiliki tujuan yang berbeda dan harus dapat berjalan secara bersamaan. Contohnya adalah operasi pemeliharaan pada sumber daya bersama, di mana banyak utas menunggu untuk menyelesaikan operasi sebelum mengakses sumber daya.
sumber
Saya pikir itu tergantung pada bagaimana sumber daya diproduksi dan dikonsumsi. Jika 5 objek kerja tersedia sekaligus dan Anda memiliki 5 objek konsumen, masuk akal untuk membangunkan semua utas menggunakan notifyAll () sehingga masing-masing dapat memproses 1 objek kerja.
Jika Anda hanya memiliki satu objek kerja yang tersedia, apa gunanya membangunkan semua objek konsumen untuk berlomba untuk satu objek itu? Yang pertama memeriksa untuk pekerjaan yang tersedia akan mendapatkannya dan semua utas lainnya akan memeriksa dan menemukan mereka tidak ada hubungannya.
Saya menemukan penjelasan yang bagus di sini . Pendeknya:
sumber
Perhatikan bahwa dengan utilitas concurrency Anda juga memiliki pilihan antara
signal()
dansignalAll()
karena metode ini disebut di sana. Jadi pertanyaannya tetap valid bahkan denganjava.util.concurrent
.Doug Lea memunculkan poin menarik dalam bukunya yang terkenal : jika a
notify()
danThread.interrupt()
terjadi pada saat yang sama, pemberitahuan itu mungkin akan hilang. Jika ini dapat terjadi dan memiliki implikasi dramatisnotifyAll()
adalah pilihan yang lebih aman meskipun Anda harus membayar biaya overhead (membangunkan terlalu banyak utas sepanjang waktu).sumber
Ringkasan singkat:
Selalu lebih suka beri tahuAll () daripada notify () kecuali Anda memiliki aplikasi paralel masif di mana sejumlah besar utas melakukan hal yang sama.
Penjelasan:
sumber: https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
Bandingkan notify () dengan notifyAll () dalam situasi yang dijelaskan di atas: aplikasi paralel besar-besaran di mana utas melakukan hal yang sama. Jika Anda memanggil notifyAll () dalam kasus itu, notifyAll () akan menginduksi bangun (yaitu penjadwalan) sejumlah besar utas, banyak dari mereka tidak perlu (karena hanya satu utas yang benar-benar dapat melanjutkan, yaitu utas yang akan diberikan kepada monitor untuk objek wait () , notify () , atau notifyAll () dipanggil), karena itu menyia-nyiakan sumber daya komputasi.
Jadi, jika Anda tidak memiliki aplikasi tempat sejumlah besar utas melakukan hal yang sama secara bersamaan, lebih baik beri tahuAll () daripada notify () . Mengapa? Karena, seperti yang sudah dijawab oleh pengguna lain di forum ini, beri tahu ()
sumber: Java SE8 API ( https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#notify-- )
Bayangkan Anda memiliki aplikasi konsumen produsen di mana konsumen siap (yaitu menunggu () ing) untuk mengkonsumsi, produsen siap (yaitu menunggu () ing) untuk memproduksi dan antrian barang (untuk diproduksi / dikonsumsi) kosong. Dalam hal ini, beri tahu () mungkin hanya membangunkan konsumen dan tidak pernah memproduksi karena pilihan yang bangun adalah sewenang - wenang . Siklus konsumen produsen tidak akan membuat kemajuan meskipun produsen dan konsumen masing-masing siap untuk memproduksi dan mengkonsumsi. Sebagai gantinya, seorang konsumen dibangunkan (yaitu meninggalkan status menunggu () ), tidak mengambil item dari antrian karena kosong, dan memberi tahu () konsumen lain untuk melanjutkan.
Sebaliknya, notifyAll () membangkitkan produsen dan konsumen. Pilihan siapa yang dijadwalkan tergantung pada penjadwal. Tentu saja, tergantung pada implementasi penjadwal, penjadwal mungkin juga hanya menjadwalkan konsumen (misalnya jika Anda memberikan prioritas yang sangat tinggi kepada konsumen). Namun, asumsi di sini adalah bahwa bahaya penjadwalan penjadwalan hanya konsumen lebih rendah daripada bahaya JVM hanya membangunkan konsumen karena penjadwal yang diimplementasikan secara wajar tidak hanya membuat keputusan sewenang - wenang . Sebaliknya, sebagian besar implementasi penjadwal membuat setidaknya beberapa upaya untuk mencegah kelaparan.
sumber
Berikut ini sebuah contoh. Menjalankannya. Kemudian ubah salah satu dari notifyAll () menjadi notify () dan lihat apa yang terjadi.
Kelas ProducerConsumerExample
Kelas dropbox
Kelas konsumen
Kelas produsen
sumber
Dari Joshua Bloch, Guru Jawa sendiri dalam Java Edisi ke-2 yang efektif:
"Butir 69: Memilih utilitas konkurensi untuk menunggu dan memberi tahu".
sumber
Saya harap ini akan menghapus beberapa keraguan.
notify () : Metode notify () bangun satu utas menunggu kunci (utas pertama yang disebut wait () pada kunci itu).
notifyAll () : Metode notifyAll () membangunkan semua utas menunggu kunci; JVM memilih salah satu utas dari daftar utas menunggu kunci dan membangunkan utas tersebut.
Dalam kasus utas tunggal menunggu kunci, tidak ada perbedaan yang signifikan antara notify () dan notifyAll (). Namun, ketika ada lebih dari satu utas yang menunggu kunci, baik di notify () dan notifyAll (), utas yang tepat dibangunkan di bawah kendali JVM dan Anda tidak dapat secara terprogram mengontrol bangunkan utas tertentu.
Pada pandangan pertama, tampaknya itu adalah ide yang baik untuk memanggil notify () untuk membangunkan satu utas; sepertinya tidak perlu membangunkan semua utas. Namun, masalah dengan memberi tahu () adalah bahwa utas yang dibangunkan mungkin bukan yang cocok untuk dibangunkan (utas mungkin menunggu beberapa kondisi lain, atau kondisinya masih belum terpenuhi untuk utas tersebut dll). Dalam hal ini , notifikasi () mungkin hilang dan tidak ada utas lain yang akan bangun yang berpotensi mengarah ke jenis kebuntuan (notifikasi hilang dan semua utas lainnya menunggu notifikasi — selamanya).
Untuk menghindari masalah ini , selalu lebih baik untuk memanggil notifyAll () ketika ada lebih dari satu utas menunggu kunci (atau lebih dari satu kondisi di mana menunggu dilakukan). Metode notifyAll () membangun semua utas, sehingga tidak sangat efisien. Namun, kehilangan kinerja ini dapat diabaikan dalam aplikasi dunia nyata.
sumber
Ada tiga negara bagian untuk utas.
Sekarang, ketika notify () dipanggil, JVM mengambil satu utas dan memindahkannya ke status BLOCKED dan karenanya ke status RUNNING karena tidak ada persaingan untuk objek monitor.
Ketika notifyAll () dipanggil, JVM mengambil semua utas dan memindahkan semuanya ke Status BLOCKED. Semua utas ini akan mendapatkan kunci objek berdasarkan prioritas. Benang yang dapat memperoleh monitor terlebih dahulu akan dapat pergi ke status MENJALANKAN terlebih dahulu dan seterusnya.
sumber
Saya sangat terkejut bahwa tidak ada yang menyebutkan masalah "bangun yang hilang" (google it).
Pada dasarnya:
KEMUDIAN Anda harus menggunakan notifyAll kecuali Anda memiliki jaminan yang terbukti bahwa kehilangan bangun tidak mungkin.
Contoh umum adalah antrian FIFO bersamaan di mana: beberapa enqueuers (1. dan 3. di atas) dapat mentransisikan antrian Anda dari kosong ke beberapa dequeuers kosong (2. di atas) dapat menunggu kondisi "antrian tidak kosong" kosong -> non-kosong harus memberi tahu dequeuers
Anda dapat dengan mudah menulis interleaving operasi di mana, mulai dari antrian kosong, 2 enqueuers dan 2 dequeuers berinteraksi dan 1 enqueuer akan tetap tidur.
Ini adalah masalah yang bisa dibandingkan dengan masalah kebuntuan.
sumber
notify()
akan bangun satu utas sementaranotifyAll()
akan bangun semua. Sejauh yang saya tahu tidak ada jalan tengah. Tetapi jika Anda tidak yakin apa yangnotify()
akan dilakukan pada utas Anda, gunakannotifyAll()
. Bekerja seperti pesona setiap saat.sumber
Semua jawaban di atas benar, sejauh yang saya tahu, jadi saya akan memberi tahu Anda sesuatu yang lain. Untuk kode produksi, Anda harus menggunakan kelas-kelas di java.util.concurrent. Ada sangat sedikit yang tidak bisa mereka lakukan untuk Anda, di bidang konkurensi di java.
sumber
notify()
memungkinkan Anda menulis kode yang lebih efisien daripadanotifyAll()
.Pertimbangkan potongan kode berikut yang dijalankan dari beberapa utas paralel:
Itu dapat dibuat lebih efisien dengan menggunakan
notify()
:Dalam kasus ini jika Anda memiliki banyak utas, atau jika kondisi loop menunggu mahal untuk dievaluasi,
notify()
akan jauh lebih cepat daripadanotifyAll()
. Misalnya, jika Anda memiliki 1000 utas, maka 999 utas akan dibangunkan dan dievaluasi setelah yang pertamanotifyAll()
, kemudian 998, lalu 997, dan seterusnya. Sebaliknya, dengannotify()
solusinya, hanya satu utas yang akan dibangunkan.Gunakan
notifyAll()
saat Anda perlu memilih utas mana yang akan melakukan pekerjaan selanjutnya:Akhirnya, penting untuk memahami bahwa dalam kasus
notifyAll()
, kode di dalamsynchronized
blok yang telah dibangunkan akan dieksekusi secara berurutan, tidak semuanya sekaligus. Katakanlah ada tiga utas menunggu dalam contoh di atas, dan utas keempat memanggilnotifyAll()
. Ketiga utas akan dibangunkan tetapi hanya satu yang akan memulai eksekusi dan memeriksa kondisiwhile
loop. Jika kondisinyatrue
, ia akan memanggilwait()
lagi, dan hanya kemudian utas kedua akan mulai mengeksekusi dan akan memeriksawhile
kondisi loop -nya , dan seterusnya.sumber
Berikut ini penjelasan yang lebih sederhana:
Anda benar bahwa apakah Anda menggunakan notify () atau notifyAll (), hasil langsungnya adalah bahwa satu utas lainnya akan mendapatkan monitor dan mulai mengeksekusi. (Dengan asumsi beberapa utas sebenarnya diblokir saat menunggu () untuk objek ini, utas lain yang tidak terkait tidak menyerap semua inti yang tersedia, dll.) Dampaknya muncul kemudian.
Misalkan utas A, B, dan C sedang menunggu objek ini, dan utas A mendapat monitor. Perbedaannya terletak pada apa yang terjadi setelah A melepaskan monitor. Jika Anda menggunakan notify (), maka B dan C masih diblokir dalam wait (): mereka tidak menunggu di monitor, mereka sedang menunggu untuk diberitahu. Ketika A melepaskan monitor, B dan C masih akan duduk di sana, menunggu pemberitahuan ().
Jika Anda menggunakan notifyAll (), maka B dan C telah maju melewati status "tunggu pemberitahuan" dan keduanya menunggu untuk mendapatkan monitor. Ketika A melepaskan monitor, B atau C akan mendapatkannya (dengan asumsi tidak ada utas lain yang bersaing untuk monitor itu) dan mulai mengeksekusi.
sumber
Jawaban ini adalah penulisan ulang grafis dan penyederhanaan jawaban yang sangat baik oleh xagyg , termasuk komentar oleh eran .
Mengapa menggunakan notifyAll, bahkan ketika setiap produk ditujukan untuk satu konsumen?
Pertimbangkan produsen dan konsumen, disederhanakan sebagai berikut.
Produsen:
Konsumen:
Asumsikan 2 produsen dan 2 konsumen, berbagi penyangga ukuran 1. Gambar berikut menggambarkan skenario yang mengarah ke jalan buntu , yang akan dihindari jika semua utas menggunakan notifyAll .
Setiap pemberitahuan ditandai dengan utas yang dibangunkan.
sumber
Saya ingin menyebutkan apa yang dijelaskan dalam Java Concurrency in Practice:
Poin pertama, apakah Memberitahu atau Memberitahu Semua?
Masalah ini dapat diselesaikan dengan menggunakan objek Kondisi kunci penguncian eksplisit, disediakan dalam jdk 5, karena menyediakan menunggu berbeda untuk setiap kondisi predikat. Di sini ia akan berperilaku dengan benar dan tidak akan ada masalah kinerja karena akan memanggil sinyal dan memastikan bahwa hanya satu utas menunggu kondisi itu
sumber
notify()
- Memilih utas acak dari set tunggu objek dan menempatkannya dalamBLOCKED
status. Sisa utas dalam set tunggu objek masih dalamWAITING
status.notifyAll()
- Memindahkan semua utas dari set tunggu objek keBLOCKED
status. Setelah Anda gunakannotifyAll()
, tidak ada utas yang tersisa dalam set menunggu objek yang dibagikan karena semuanya sekarang dalamBLOCKED
keadaan dan tidak dalamWAITING
keadaan.BLOCKED
- Diblokir untuk akuisisi kunci.WAITING
- menunggu pemberitahuan (atau diblokir untuk penyelesaian gabungan).sumber
Diambil dari blog di Java Efektif:
Jadi, yang saya pahami adalah (dari blog yang disebutkan di atas, komentar oleh "Yann TM" pada jawaban yang diterima dan dokumen Java ):
sumber
Lihatlah kode yang diposting oleh @xagyg.
Misalkan dua benang yang berbeda menunggu dua kondisi yang berbeda:
The Thread pertama sedang menunggu
buf.size() != MAX_SIZE
, dan thread kedua sedang menunggubuf.size() != 0
.Misalkan pada titik tertentu
buf.size()
tidak sama dengan 0 . JVM memanggilnotify()
alih-alihnotifyAll()
, dan utas pertama diberi tahu (bukan yang kedua).Utas pertama dibangunkan, memeriksa
buf.size()
yang mungkin kembaliMAX_SIZE
, dan kembali menunggu. Utas kedua tidak dibangunkan, terus menunggu dan tidak meneleponget()
.sumber
notify()
bangun utas pertama yang memanggilwait()
objek yang sama.notifyAll()
membangunkan semua utas yang memanggilwait()
objek yang sama.Utas prioritas tertinggi akan berjalan terlebih dahulu.
sumber
notify()
itu bukan " utas pertama ".beri tahu akan memberi tahu hanya satu utas yang dalam kondisi menunggu, sedangkan beri tahu semua akan memberi tahu semua utas dalam status tunggu sekarang semua utas yang diberitahukan dan semua utas yang diblokir memenuhi syarat untuk kunci, di mana hanya satu yang akan mendapatkan kunci dan semua yang lain (termasuk mereka yang dalam keadaan menunggu lebih awal) akan dalam keadaan diblokir.
sumber
Untuk meringkas penjelasan terperinci yang sangat baik di atas, dan dengan cara paling sederhana yang dapat saya pikirkan, ini disebabkan oleh keterbatasan monitor bawaan JVM, yang 1) diperoleh pada seluruh unit sinkronisasi (blok atau objek) dan 2) tidak membeda-bedakan kondisi tertentu yang sedang menunggu / diberitahu pada / tentang.
Ini berarti bahwa jika beberapa utas menunggu pada kondisi yang berbeda dan memberitahukan () digunakan, utas yang dipilih mungkin bukan yang akan membuat kemajuan pada kondisi yang baru terpenuhi - menyebabkan utas tersebut (dan utas lainnya yang masih menunggu yang akan dapat untuk memenuhi kondisi, dll.) tidak dapat membuat kemajuan, dan akhirnya kelaparan atau hangup program.
Sebaliknya, notifyAll () memungkinkan semua utas tunggu akhirnya mendapatkan kembali kunci dan memeriksa kondisinya masing-masing, sehingga akhirnya memungkinkan kemajuan dibuat.
Jadi, beri tahu () dapat digunakan dengan aman hanya jika utas menunggu dijamin untuk memungkinkan kemajuan harus dibuat jika dipilih, yang secara umum puas ketika semua utas dalam monitor yang sama memeriksa hanya satu dan kondisi yang sama - yang cukup langka kasus dalam aplikasi dunia nyata.
sumber
Ketika Anda memanggil wait () dari "objek" (mengharapkan kunci objek diperoleh), magang ini akan melepaskan kunci pada objek itu dan membantu adalah utas lain untuk mengunci pada "objek" ini, dalam skenario ini akan ada lebih dari 1 utas menunggu "sumber daya / objek" (mengingat utas lainnya juga mengeluarkan tunggu pada objek yang sama di atas dan di ujung jalan akan ada utas yang mengisi sumber daya / objek dan memanggil notify / notifyAll).
Di sini, ketika Anda mengeluarkan pemberitahuan tentang objek yang sama (dari sisi yang sama / sisi lain dari proses / kode), ini akan merilis utas tunggal yang diblokir dan menunggu (tidak semua utas tunggu - utas yang dirilis ini akan dipilih oleh JVM Thread Penjadwal dan semua proses mendapatkan kunci pada objek sama seperti biasa).
Jika Anda hanya memiliki satu utas yang akan membagikan / mengerjakan objek ini, tidak masalah untuk menggunakan metode notify () sendirian dalam implementasi wait-notify Anda.
jika Anda berada dalam situasi di mana lebih dari satu utas membaca dan menulis pada sumber daya / objek berdasarkan logika bisnis Anda, maka Anda harus mencari notifyAll ()
sekarang saya mencari bagaimana tepatnya jvm mengidentifikasi dan memutus utas menunggu ketika kami mengeluarkan notify () pada sebuah objek ...
sumber
Meskipun ada beberapa jawaban kuat di atas, saya terkejut dengan jumlah kebingungan dan kesalahpahaman yang telah saya baca. Ini mungkin membuktikan gagasan bahwa seseorang harus menggunakan java.util.concurrent sebanyak mungkin alih-alih mencoba menulis kode konkuren yang rusak sendiri. Kembali ke pertanyaan: untuk meringkas, praktik terbaik hari ini adalah HINDARI beri tahu () dalam SEMUA situasi karena masalah bangun yang hilang. Siapa pun yang tidak memahami ini tidak boleh diizinkan untuk menulis kode konkurensi misi kritis. Jika Anda khawatir tentang masalah penggembalaan, satu cara aman untuk mencapai bangun satu utas pada satu waktu adalah dengan: 1. Membuat antrean tunggu yang eksplisit untuk utas tunggu; 2. Mintalah setiap utas dalam antrian menunggu pendahulunya; 3. Minta setiap utas panggilan notifyAll () saat selesai. Atau Anda dapat menggunakan Java.util.concurrent. *,
sumber
Runnable
implementasi) memproses konten antrian. Iniwait()
kemudian digunakan setiap kali antrian kosong. Dannotify()
disebut ketika informasi ditambahkan. -> dalam kasus seperti itu hanya ada 1 utas yang pernah memanggilwait()
, maka bukankah itu terlihat agak konyol untuk digunakannotifyAll()
jika Anda tahu hanya ada 1 utas menunggu.Bangun semua tidak banyak berarti di sini. tunggu beri tahu dan beri tahu semua, semua ini dimasukkan setelah memiliki monitor objek. Jika sebuah utas sedang dalam tahap menunggu dan notifikasi dipanggil, utas ini akan mengambil kunci dan tidak ada utas lain pada saat itu yang bisa mengambil kunci itu. Jadi akses bersamaan tidak bisa terjadi sama sekali. Sejauh yang saya tahu panggilan apa pun untuk menunggu memberi tahu dan memberitahukan semua dapat dilakukan hanya setelah mengambil kunci pada objek. Perbaiki saya jika saya salah.
sumber