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