вызов thread.start () в собственном конструкторе

законно ли потоку вызывать this.start () внутри своего собственного конструктора? и если да, то какие потенциальные проблемы это может вызвать? Я понимаю, что объект не будет полностью инициализирован до тех пор, пока конструктор не завершит работу, но есть ли другие проблемы, кроме этого?


person Community    schedule 17.09.2008    source источник


Ответы (7)


Из соображений безопасности памяти не следует предоставлять ссылку на объект или поля этого объекта другому потоку из его конструктора. Предполагая, что ваш настраиваемый поток имеет переменные экземпляра, запустив его из конструктора, вы гарантированно нарушите руководящие принципы модели памяти Java. Для получения дополнительной информации см. методы безопасного строительства Брайана Гетца.

person Heath Borders    schedule 17.09.2008
comment
Комментарии к этому ответу? Это не неправильно. - person Heath Borders; 18.09.2008
comment
Проголосовать против? Если вы думаете, что это неправильно, укажите на сайте источник. Это верно. - person Heath Borders; 02.07.2012
comment
Ответ вовсе не консервативный. Если вы не используете конструктор Runnable, вы можете ссылаться на this из Thread#run до завершения конструктора, что нарушает правила безопасности памяти. Таким образом, вы должны быть осторожны. - person Heath Borders; 30.12.2015
comment
После внимательного прочтения вопроса я понимаю, что речь идет о вызове start в том же конструкторе Thread. Я представил себе другой сценарий, и хотя мой сценарий работает вокруг обсуждаемых здесь проблем, они не имеют отношения к контексту этого вопроса. Пожалуйста, примите мои извинения. - person Edwin Buck; 30.12.2015
comment
Совершенно не беспокойтесь. Я рад, что мы это прояснили. Я боялся, что неправильно понял что-то фундаментальное. :) - person Heath Borders; 31.12.2015

Вы также увидите странные проблемы, если класс Thread когда-либо будет подклассифицирован. В этом случае вы закончите тем, что поток будет запущен уже после выхода super (), и все, что подкласс может сделать в своем конструкторе, может быть недопустимым.

@bill barksdale Если поток уже запущен, повторный вызов start даст вам исключение IllegalThreadStateException, вы не получите 2 потока.

person John Gardner    schedule 17.09.2008

Я предполагаю, что вы хотите сделать это, чтобы ваш код был менее подробным; вместо того, чтобы сказать

Thread t = new CustomThread();
t.start();
activeThreads.add(t);

ты можешь просто сказать

activeThreads.add( new CustomThread() );

Мне также нравится меньше многословия, но я согласен с другими респондентами, что вам не следует этого делать. В частности, это нарушает конвенцию; любой, знакомый с Java и читающий второй пример, предположит, что поток еще не запущен. Что еще хуже, если они напишут свой собственный код потоковой передачи, который каким-то образом взаимодействует с вашим, тогда некоторые потоки должны будут вызывать start, а другие - нет.

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

Однако, если вас не волнуют условности и вы ненавидите лишнее многословие, тогда продолжайте; это не вызовет никаких проблем, даже если вы попытаетесь позвонить start несколько раз по ошибке.

person Eli Courtwright    schedule 17.09.2008

Кстати, если кто-то хочет снизить уровень детализации и при этом сохранить конструктор с его "стандартной" семантикой, можно создать фабричный метод:

activeThreads.add( CustomThread.newStartedThread() );
person millenomi    schedule 17.09.2008
comment
какое отношение это имеет к вопросу ОП? - person subsub; 10.05.2012

Это законно, но неразумно. Часть экземпляра Thread будет полностью инициализирована, а ваш конструктор - нет. Существует очень мало причин для расширения Thread, и использование подобных трюков не поможет вашему коду.

person Tom Hawtin - tackline    schedule 17.09.2008

Это "законно", но я думаю, что самая важная проблема заключается в следующем: класс должен делать одно и делать это хорошо.

Если ваш класс использует поток внутри, то существование этого потока не должно быть видно в общедоступном API. Это позволяет улучшить, не затрагивая общедоступный API. Решение: расширьте Runnable, а не Thread.

Если ваш класс предоставляет общие функции, которые в данном случае выполняются в потоке, вы не хотите ограничивать себя всегда созданием потока. То же решение здесь: расширить Runnable, а не Thread.

Для меньшей многословности я поддерживаю предложение использовать фабричный метод (например, Foo.createAndRunInThread ()).

person volley    schedule 17.09.2008

Юридически ... да (с оговорками, упомянутыми в другом месте). Целесообразно ... нет.

Я просто запах, которого легко избежать. Если вы хотите, чтобы ваш поток запускался автоматически, просто сделайте это, как Хайнц Кабуц.

public class ThreadCreationTest {
  public static void main(String[] args) throws InterruptedException {
    final AtomicInteger threads_created = new AtomicInteger(0);
    while (true) {
      final CountDownLatch latch = new CountDownLatch(1);
      new Thread() {
        { start(); } // <--- Like this ... sweet and simple.
        public void run() {
          latch.countDown();
          synchronized (this) {
            System.out.println("threads created: " +
                threads_created.incrementAndGet());
            try {
              wait();
            } catch (InterruptedException e) {
              Thread.currentThread().interrupt();
            }
          }
        }
      };
      latch.await();
    }
  }
}
person OldCurmudgeon    schedule 03.03.2012