.- Supongo que era para las pruebas, pero como todos estaban así... Yo no los dejo así ni siquiera, e incluso especialmente, para las pruebas, por que es cuando más excepciones me saltan :).
.- Synchronised es necesario por que estas accediendo a un recurso compartido: el bloqueo del objeto.
.- El problema en si no es que los otros threads se mueran antes de terminar, cosa que no puede ocurrir a no ser que hagas un Sysm.exit() o sea threads del tipo daemon, si no que acaben tan rapido que la JVM no vacíe el buffer de la salida estandar antes de salir. No es habitual, pero a mi me ha pasado alguna vez cuando los tiempos van muy ajustados y dependiendo de la carga etc. De hecho, dado que no podias llamar al notify, si el wait hubiera funcionado tu programa no habría acabado nunca y se habría quedado "colgado" con thread esperando en un wait.
.- No me queda muy claro lo que explicas en el último punto, por que el notify no se llama a través de un "método en espera". Quizá te refieres a que se llama al método del objeto que esta "en espera" (wait), pero es lo "lógico" puesto que el notify se ha de hacer sobre el mismo objeto de bloqueo (monitor) que esta esperando. Si no, ¿Como sabrías qué objetos tienes que "despertar"?
El de la sincronización y los Thread es un tema complejo, así que es bueno hacer estos experimentos y confirmar o descartar lo que creemos que sabemos. Yo aun me encuentro de vez en cuando cosas que me sorprenden
.