Originariamente inviato da Maxton
ho capito più o meno tutti, tranne per cosa intendi per lock, un wait() non è un lock?
No, ogni oggetto possiede un "lock" (intrinsic lock e nella documentazione più tecnica è chiamato object's monitor). Che può essere acquisito da un solo thread per volta (da qui appunto l'effetto della mutua-esclusione tra thread).
Il lock dell'oggetto lo si acquisisce solo usando la parola chiave synchronized (applicato a un metodo o a un blocco).

I wait/notify agiscono sulla condition queue associata al lock. Ossia c'è una "coda" in cui più thread possono stare in attesa di essere "svegliati".
Quando si esegue il wait(), il thread va in wait ma allo stesso tempo rilascia il lock. Quando esce dal wait(), allora compete di nuovo per riottenere il lock. E il problema è che per questioni interne alla JVM è possibile avere risvegli "spurii" e per tale motivo in genere il wait() si mette in un while che controlla una certa condizione (es. pensa ad una coda "sincronizzata" e "bloccante", il get si blocca finché la coda è vuota, cioè: while (codaVuota) fai il wait).
Quindi il tuo modo di usare il wait non è nemmeno appropriato al 100%.

E comunque se si fa un wait su un reference xyz, il/i notify vanno fatti su quello stesso identico reference, non su un altro.