Не нарушен ли метод Sun Thread.join, потому что он синхронизирует использование объекта Thread? - PullRequest
1 голос
/ 02 мая 2009

Как при запуске тестовых программ, так и при просмотре исходного кода становится ясно, что метод, реализованный Sun, не просто возвращает время указанному потоку, но фактически сначала пытается получить монитор объекта потока. В частности, метод реализован как «синхронизированный».

Обратите внимание, что для методов ожидания и уведомления также требуется монитор, но в отличие от соединения, вызывающий абонент обязан получить монитор перед выполнением вызова, и в документации об этом ясно сказано. Тот факт, что объединение зависит от монитора, не задокументирован в Javadocs, хотя, возможно, естественно сделать вывод.

Достаточно ли ясна документация?

Кроме того, если поток по какой-то причине не может получить монитор, он может зависнуть, возможно, навсегда. Во время ожидания монитора поток не прервется и не сгенерирует исключение InterruptedException, как описано в документации. С другой стороны, неясно, почему поток не сможет получить монитор, кроме как в случае ошибки программирования.

Разумно ли беспокоиться о конфликтах из-за монитора?

Наконец, кажется неуместным делать операцию тайм-аута зависимой от получения монитора, если только не может быть гарантировано, что сама задача получения монитора истечет.

Разумная реализация в зависимости от монитора для join ()? Можно ли вообще это реализовать другим способом?

Ответы [ 2 ]

10 голосов
/ 02 мая 2009

Thread.join вызывает wait, что освобождает монитор. Поскольку это означает, что «присоединяющийся» поток не блокирует также и любой другой поток от вызова join, я подозреваю, что это отвечает на большинство других ваших запросов. Это не мешает другому абоненту синхронизироваться на мониторе потока (о радости публичных мониторов), но это означает, что общий случай работает нормально.

Просто чтобы продемонстрировать, что ваша первая точка неверна, вот пример, который создает 10 потоков, каждый из которых ожидает в основном потоке в течение 5 секунд. (Пожалуйста, игнорируйте ужасное исключение глотания и злоупотребления Date. Он предназначен только для изучения поведения потоков.)

import java.util.*;

public class Test
{
    public static void main(String[] args) throws Exception
    {
        for (int i=0; i < 10; i++)
        {
            new Thread(new ThreadJoiner(Thread.currentThread(), i))
                     .start();
        }
        try
        {
            Thread.sleep(10000);
        }
        catch (InterruptedException e) {}
    }

    private static class ThreadJoiner implements Runnable
    {
        private final Thread threadToJoin;
        int id;

        public ThreadJoiner(Thread threadToJoin, int id)
        {
            this.threadToJoin = threadToJoin;
            this.id = id;
        }

        public void run()
        {
            try
            {
                System.out.println("Thread " + id +
                                   " waiting at " + new Date());
                threadToJoin.join(5000);
                System.out.println("Thread " + id +
                                   " finished waiting at " + new Date());
            }
            catch (InterruptedException e) {}
        }
    }
}

Если вы запустите это, вы увидите, что все потоки начинают и заканчивают свои ожидания практически одновременно. Вы не достигаете цели "в шахматном порядке", как если бы ваши проблемы были обоснованными.

5 голосов
/ 02 мая 2009

ясно, что метод, реализованный Sun, не просто возвращает время указанному потоку, но фактически сначала пытается получить монитор объекта потока.

Он не уступает присоединенному потоку, он просто ожидает с предположением, что в какой-то момент поток будет завершен. Наличие join () в потоке не повышает его вероятность выполнения по сравнению с любым другим потоком, готовым к запуску.

  1. Если все N потоков пытаются присоединиться к одному и тому же потоку, и все они указывают одинаковое время ожидания T, то один из потоков в конечном итоге ожидает не менее N * T мс. Другими словами, каждый поток должен «ждать своей очереди», чтобы выполнить свое ожидание. Разумно ли для отдельных потоков выполнять соединения последовательно, а не параллельно?

Потоки предназначены для одновременной работы. Если они все ждут, они делают это одновременно. Ожидающий поток не заставляет другой поток ждать дольше.

0,2. Возможно, что поток, который входит в соединение с ненулевым таймаутом, может никогда не вернуться.

нет, если только вы не собираетесь это сделать.

Это потому, что нет гарантии, что монитор когда-либо станет доступным.

Ситуация, которую вы предлагаете, может возникнуть, только если поток получает блокировку для потока и затем удерживает ее навсегда, не ожидая. Это ошибка программирования. ИМХО Никогда нельзя получать блокировку на объект потока напрямую.

Если поток получает свой собственный монитор перед блокировкой операции ввода-вывода и эта операция зависает, то любой поток, который пытается присоединиться к потоку, также будет зависать.

Java не защищает вас от вредоносного кода в вашей собственной JVM.

Разумно ли для операции с явным временем ожидания зависать бесконечно?

Если он заблокирован на неопределенный срок, да.

0,3. Чтобы написать правильную программу, которая использует метод, вызывающая сторона должна заранее знать, может ли целевой поток или какой-либо другой поток держать монитор.

Никогда не блокируйте объект потока, нет необходимости в этом, и у вас не будет этой проблемы. Если вы хотите начать смотреть на все так, как могли бы сбить с толку других разработчиков или себя, то «Потоки» - не то место, где вы бы начали ИМХО.

Например, рассмотрим, что происходит, если поток 1 выполняет какую-то обработку, затем поток 2 присоединяется к потоку 1 с тайм-аутом 0, а затем поток 3 пытается присоединиться к потоку 1 с тайм-аутом 10 мс. Соединение с тайм-аутом 0 означает, что поток 2 будет ожидать выхода из потока 1. Но поток 3 не может начать свое ожидание, пока поток 2 не освободит монитор,

Поток 2 освобождает монитор, как только вызывается ожидание. Он не может ждать и удерживать монитор одновременно.

так эффективно ожидание потока 3, равное 10 мс, было тихо преобразовано в неопределенное ожидание, что не было тем, что предполагал вызывающий.

нет. см. предыдущие комментарии.

Не требует ли вызывающая сторона знать детали о реализации, нарушающей принцип инкапсуляции?

Было бы.

0,4. Если поток заблокирован из-за того, что он не может получить монитор, он не прервется и не сгенерирует исключение InterruptedException, как описано в документации. Таким образом, поток может не только ждать дольше, чем ожидалось, либо даже неопределенно долго, он может полностью перестать отвечать, что приведет к зависанию всей программы. Разумно ли, чтобы прерываемая операция перестала отвечать на запросы?

да. Но это очень редкое состояние. Если вы используете код в том виде, в котором он написан, ситуация, на которую вы ссылаетесь, будет существовать не более нескольких миллисекунд.

В целом, кажется неуместным делать операцию тайм-аута зависимой от получения монитора, если только не может быть гарантировано, что сама задача получения монитора истечет. Прервано ли соединение?

Вы можете делать то, что предлагаете, с более поздними библиотеками параллелизма Java 5.

Однако я предлагаю вам не предполагать, что тайм-ауты гарантированно будут с точностью до миллисекунды. currentTimeMillis (), используемый этим методом, является точным в Windows XP только до 16 мс, а время ожидания / сна обычно на 2 мс больше, чем должно быть для небольших тайм-аутов в Linux.

ИМХО. Если вам нужна точность более 40 мс, у вас могут возникнуть трудности, однако, если обойти это, вы обнаружите, что это не должно быть проблемой.

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