La différence importante entre l'état bloqué et l'état d'attente est l'impact sur l'ordonnanceur. Un thread dans un état bloqué se bat pour un verrou ; ce thread compte toujours comme quelque chose que le planificateur doit gérer, et il est possible qu'il soit pris en compte dans les décisions du planificateur concernant le temps à accorder aux threads en cours d'exécution (afin de donner une chance aux threads qui bloquent le verrou).
Une fois qu'un thread est dans l'état d'attente, la pression qu'il exerce sur le système est minimisée et le planificateur n'a pas à s'en préoccuper. Il reste en sommeil jusqu'à ce qu'il reçoive une notification. À l'exception du fait qu'il maintient un thread du système d'exploitation occupé, il est entièrement hors jeu.
C'est la raison pour laquelle l'utilisation de notifyAll est loin d'être idéale, car elle réveille un tas de threads qui étaient auparavant joyeusement dormants et qui n'imposaient aucune charge au système. La plupart d'entre eux bloqueront jusqu'à ce qu'ils puissent acquérir le verrou, constateront que la condition qu'ils attendent n'est pas vraie et recommenceront à attendre. Il serait préférable de ne notifier que les threads qui ont une chance de progresser.
(L'utilisation de ReentrantLock au lieu de verrous intrinsèques vous permet d'avoir plusieurs conditions pour un seul verrou, de sorte que vous pouvez vous assurer que le thread notifié est celui qui attend une condition particulière, évitant ainsi le bug de la notification perdue dans le cas où un thread est notifié pour quelque chose sur lequel il ne peut pas agir).