Semantiğini wait()
koruyarak, arayan iş parçacığını askıya alarak senkronize edilmiş bir bloğun dışına çağırmak mümkün olsaydı potansiyel hasar nedir ?
Somut bir örneklewait()
senkronize edilmiş bir bloğun dışına çağrılabilirse hangi sorunlarla karşılaşacağımızı gösterelim .
Bir engelleme kuyruğu uygulayacağımızı varsayalım (Biliyorum, API'da zaten bir tane var :)
İlk denemede (senkronizasyon olmadan) aşağıdaki satırlar boyunca bir şeyler görünebilir
class BlockingQueue {
Queue<String> buffer = new LinkedList<String>();
public void give(String data) {
buffer.add(data);
notify(); // Since someone may be waiting in take!
}
public String take() throws InterruptedException {
while (buffer.isEmpty()) // don't use "if" due to spurious wakeups.
wait();
return buffer.remove();
}
}
Potansiyel olarak ne olabilir:
Bir tüketici iş parçacığı arar take()
ve görür buffer.isEmpty()
.
Tüketici iş parçacığı aramaya başlamadan önce wait()
, bir üretici iş parçacığı gelir ve bir dolu çağırır give()
, yani,buffer.add(data); notify();
Tüketici iplik şimdi arayacak wait()
(ve kaçırmaknotify()
aradı olduğunu).
Şanssızsa, üretici ipliği give()
, tüketici ipliğinin asla uyanmadığı ve daha fazla kilitlememizin bir sonucu olarak daha fazla üretmez .
Sorunu anladıktan sonra çözüm açıktır: ve ile asla çağrılmadığından synchronized
emin olmak notify
için kullanın .isEmpty
wait
Ayrıntılara girmeden: Bu senkronizasyon sorunu evrenseldir. Michael Borgwardt'ın işaret ettiği gibi, bekle / bildir tamamen dişler arasındaki iletişim ile ilgilidir, bu yüzden her zaman yukarıda tarif edilene benzer bir yarış durumu elde edersiniz. Bu nedenle "yalnızca senkronize içinde bekle" kuralı uygulanır.
@Willie tarafından yayınlanan bağlantıdan bir paragraf bunu oldukça iyi özetliyor:
Garson ve bildirim yapan kişinin yüklemin durumu konusunda hemfikir olduğu konusunda mutlak bir garantiye ihtiyacınız vardır. Garson, uyumadan ÖNCE yüklemin durumunu bir noktada hafifçe kontrol eder, ancak uyumaya gittiğinde yüklemin doğruluğuna bağlıdır. Bu iki olay arasında, programı bozabilecek bir güvenlik açığı vardır.
Üreticinin ve tüketicinin üzerinde anlaşmaya varmasının gerekliliği yukarıdaki örnektir buffer.isEmpty()
. Anlaşma, bekleme ve bildirimin synchronized
bloklar halinde yapılmasını sağlayarak çözülür .
Bu yazı burada bir makale olarak yeniden yazılmıştır: Java: Neden bekleme senkronize bir blokta çağrılmalıdır