Вызов thread.start() в своем собственном конструкторе
Законно ли для потока вызывать this.start() внутри своего собственного конструктора? и если да, то какие потенциальные проблемы это может вызвать? Я понимаю, что объект не будет полностью инициализирован до тех пор, пока конструктор не завершится, но кроме этого есть ли другие проблемы?
7 ответов
По соображениям безопасности памяти не следует предоставлять ссылку на объект или поля этого объекта другому потоку из его конструктора. Предполагая, что ваш пользовательский поток имеет переменные экземпляра, запустив его из конструктора, вы гарантированно нарушите рекомендации модели памяти Java. См . Безопасные Строительные Методы Брайана Гетца для получения дополнительной информации.
Вы также увидите странные проблемы, если класс Thread когда-либо будет разделен на подклассы. В этом случае вы получите поток, работающий уже после выхода из super(), и все, что может сделать подкласс в своем конструкторе, может быть недопустимым.
@bill barksdale Если поток уже запущен, повторный вызов start возвращает исключение IllegalThreadStateException, а 2 потока - нет.
Кстати, если кто-то хочет получить более детальное слово и при этом сохранить конструктор со своей "стандартной" семантикой, можно создать метод фабрики:
activeThreads.add( CustomThread.newStartedThread() );
Я предполагаю, что вы хотите сделать это, чтобы сделать ваш код менее многословным; вместо того чтобы сказать
Thread t = new CustomThread();
t.start();
activeThreads.add(t);
ты можешь просто сказать
activeThreads.add( new CustomThread() );
Мне также нравится меньше многословия, но я согласен с другими респондентами, что вы не должны этого делать. В частности, это нарушает соглашение; любой, кто знаком с Java и читает второй пример, предположит, что поток не был запущен. Хуже того, если они пишут свой собственный многопоточный код, который каким-то образом взаимодействует с вашим, то некоторые потоки должны будут вызывать start
а другие не будут.
Это может показаться неубедительным, когда вы работаете самостоятельно, но в конечном итоге вам придется работать с другими людьми, и полезно развить хорошие привычки кодирования, чтобы вам было легко работать с другими людьми и писать код с Стандартные соглашения.
Однако, если вы не заботитесь о соглашениях и ненавидите лишнее многословие, тогда продолжайте; это не вызовет никаких проблем, даже если вы попытаетесь позвонить start
несколько раз по ошибке.
Законно... да (с оговорками, как упоминалось в другом месте). Желательно... нет.
Я просто запах, который вы можете слишком легко избежать. Если вы хотите, чтобы ваш поток запускался автоматически, просто сделайте это, как Heinz Kabutz.
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();
}
}
}
Это "законно", но я думаю, что самая важная проблема заключается в следующем: класс должен делать одну вещь и делать это хорошо.
Если ваш класс использует поток внутренне, то существование этого потока не должно быть видно в общедоступном API. Это позволяет улучшить, не затрагивая общедоступный API. Решение: расширить Runnable, а не Thread.
Если ваш класс предоставляет общие функциональные возможности, которые в данном случае выполняются в потоке, то вы не хотите ограничивать себя созданием потока. То же решение здесь: расширить Runnable, а не Thread.
Для меньшей детализации я подкрепляю предложение использовать фабричный метод (например, Foo.createAndRunInThread()).
Это законно, но не разумно. Часть Thread экземпляра будет полностью инициализирована, но ваш конструктор может этого не делать. Существует очень мало причин для расширения Thread, и такие хитрости не помогут вашему коду.