Я написал статью про wait-notify примитивы. Закончил я на том, что не стоит делать свои велосипеды, а лучше использовать более высокоуровневые абстракции. Давайте посмотрим, как этот вопрос решает Srping 5.

Скачаем исходники Spring и поищем в них использование wait, notify.

В spring core я нашел только одно использование wait в org.springframework.util.ConcurrencyThrottleSupport#beforeAccess:

protected void beforeAccess() {
    if (this.concurrencyLimit == NO_CONCURRENCY) {
        throw new IllegalStateException(
                "Currently no invocations allowed - concurrency limit set to NO_CONCURRENCY");
    }
    if (this.concurrencyLimit > 0) {
        boolean debug = logger.isDebugEnabled();
        synchronized (this.monitor) {
            boolean interrupted = false;
            while (this.concurrencyCount >= this.concurrencyLimit) {
                if (interrupted) {
                    throw new IllegalStateException("Thread was interrupted while waiting for invocation access, " +
                            "but concurrency limit still does not allow for entering");
                }
                if (debug) {
                    logger.debug("Concurrency count " + this.concurrencyCount +
                            " has reached limit " + this.concurrencyLimit + " - blocking");
                }
                try {
                    this.monitor.wait();
                }
                catch (InterruptedException ex) {
                    // Re-interrupt current thread, to allow other threads to react.
                    Thread.currentThread().interrupt();
                    interrupted = true;
                }
            }
            if (debug) {
                logger.debug("Entering throttle at concurrency count " + this.concurrencyCount);
            }
            this.concurrencyCount++;
        }
    }
}

На мой взгляд код прекрасен. Вместо 0 используется NO_CONCURRENCY. Мелочь, а улучшает читаемость кода. При этом в выражении this.concurrrencyLimit > 0 константа не используется. И это логично, странно иметь что-то большее отсутствия конкурентности. Метод beforeAccess ждет, когда уменьшится concurrencyCount, это происходит в методе afterAccess, а потом пропускает поток выполняться. Выполнение доступа к ресурсу может занимать очень долгое время и не стоит полить переменную зря.

Сейчас хорошо бы рассмотреть другой throttler, например из Guava, но я это сделаю в другой раз.