Пускает ли Thread # join () другие потоки через синхронизированные блоки? - PullRequest
0 голосов
/ 06 марта 2019

Метод Object#wait() обладает этим забавным свойством, которое позволяет другим потокам входить в его синхронизированный блок, пока он заблокирован в нем. Пример (предположим, что поток 1 запускается первым):

Тема 1:

synchronized(someLock)
{
    wait();
}

Тема 2:

synchronized(someLock)
{
    notify();
}

Тот факт, что поток 2 может активизировать поток 1, означает, что поток 2 вошел в синхронизированный блок, даже если какой-то другой поток находился в синхронизированном блоке того же объекта. Это нормально для меня, но мне интересно, если это происходит только для Object#wait() или для всех методов, которые заставили бы поток "ждать" (Thread#sleep, Thread#join). В моем случае меня волнует Thread#join, потому что, если поведение будет таким же, как Object#wait(), это нарушит мой код:

private void waitForClose()
{
    try
    {
        // if one thread is waiting in join the other will wait on the semaphore
        synchronized(joinLock)
        {
            if(outputThread != null && Thread.currentThread() != outputThread)
                outputThread.join();
            outputThread = null;

            if(inputThread != null && Thread.currentThread() != inputThread)
                inputThread.join();
            inputThread = null;
        }
    }
    catch(InterruptedException ex)
    {
        logger.error("Interrupted Exception while waiting for thread to join in " + name, ex);
    }
}

Так возможно ли, что несколько потоков входят в этот синхронизированный блок из-за вызова соединения, переводящего поток в состояние ожидания?

1 Ответ

2 голосов
/ 06 марта 2019

Прежде всего, механизмы wait и notify не такие уж смешные. Это самый элементарный способ координации двух или более потоков в Java. Важно понимать, что здесь происходит:

Тема 1:

synchronized (someLock) {
  System.out.println("Thread 1 going to wait ...");
  someLock.wait();
  System.out.println("Threads 1 got notified.");
}

Тема 2:

synchronized (someLock) {
  System.out.println("Notifying");
  someLock.notify();
  System.out.println("Exiting block.");
}

Вызов wait() снимет блокировку, позволяя другому потоку завладеть ею. На этом этапе поток 1 не сможет продолжить , даже если он получит уведомление . Эта документация ясно заявляет это:

Пробужденная нить не сможет продолжаться до текущего поток снимает блокировку этого объекта.

Таким образом, только после того, как поток 2 выйдет из блока synchronized, поток 1 продолжит работу с кодом после wait().

Thread.join() - синтаксический сахар, вспомогательный метод, который под капотом использует те же самые wait и notify / notifyAll(). На самом деле javadoc предостерегает от использования wait и notify на Thread объектах, чтобы не мешать этому механизму.

Эта реализация использует цикл вызовов this.wait, обусловленных this.isAlive. Поскольку поток завершает, метод this.notifyAll вызывается. Рекомендуется, чтобы приложения не использовали ожидание, уведомление или notifyAll on экземпляров Thread.

Thread.sleep() не имеет отношения к wait и notify. Он не нуждается в блокировке объекта и не должен находиться в блоке synchronized.

Ваш код, кажется, синхронизируется с объектом с именем joinLock, в то время как outputThread.join() будет синхронизироваться и ожидать объекта outputThread. Они не связаны. Во всяком случае, вы можете рисковать блокировкой, если outputThread синхронизируется с joinLock. Без кода для outputThread я не могу сказать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...