2009-05-11 2 views
2

С учетом модели памяти J5 + (JSR-133) приведен следующий код, безопасный по потоку и допустимый?Могу ли я запустить поток из конструктора?

И если это безопасно, приемлемо ли это в некоторых ситуациях?

public final class BackgroundProcessor 
extends Object 
implements Runnable 
{ 

public BackgroundProcessor(...) { 
    super(); 

    ... 

    new Thread(this).start(); 
    } 

public void run() { 
    ... 
    } 
} 

Как я прочитал новый JMM спецификации, инициируя нить создает происходит, прежде, чем отношения с чем инициируемая нить делает.

Предположим, что объект имеет переменные частного члена, установленные в конструкторе и используемые в run().

И класс отмечен как окончательный, чтобы предотвратить сюрпризы подкласс.

Примечание: Существует аналогичный вопрос здесь, но это имеет другой угол: calling thread.start() within its own constructor

+2

Я бы предположил, что не стоит ничего делать в конструкторе за пределами поля инициализации. Это сделает ваш код очень трудным для тестирования. Вот полезная статья, которая объясняет это намного лучше, чем я могу: http://misko.hevery.com/code-reviewers-guide. Я изначально поставил это как ответ, но на самом деле он не отвечает на ваш вопрос, поэтому вместо этого он является комментарием. – bm212

+0

@ bm212: Хорошая ссылка (+1). Хотя это специальная конструкция, для которой я пытаюсь определить, нужен ли рефакторинг и на каком уровне приоритета. –

ответ

2

Ну, если вы специально не согласны с Брайаном Гетцем по проблеме параллелизма Java, я бы предположил, что это неправильно. Хотя его точные слова «лучше не делать ...», я все еще прислушивался к его советам. От JCIP, p.42:

«А распространенная ошибка, что может позволить „это“эталонный побег во время строительства является запустить поток из конструктора [...] Там нет ничего плохого с создавая нить в конструкторе, но лучше не к начала нить сразу. Вместо этого выставить старт или инициализировать метод ...»

Обновление: Просто уточните, почему это проблема. Хотя гарантированно существует барьер памяти между вызовом Thread.start() и фактическим запуском метода thread(), этот барьер между тем, что происходит во время конструктора после, вызывает Thread.start() , Поэтому, если еще какие-то другие переменные еще должны быть установлены или если JVM выполняет некоторую «быструю работу», то ни один из них не может быть замечен другим потоком: т. Е. Объект может быть замечен другой поток в неполном состоянии.

Кстати, помимо того, действительно ли он разрушает JMM, он также просто немного похож на «странное дело» в конструкторе.

+1

Да, но почему это плохо? И если бы он был сломан, я бы ожидал, что формулировка типа «вы не должны начинать нить в своем конструкторе», а не «лучше не ...». По мнению Эско, я склонен думать, что спецификация JMM определяет поток, запущенный в конце конструктора, как поточно-безопасный. Кроме того, является ли цитата WRT пересмотренной JMM или старой? –

+0

Вопрос о том, относится ли Goetz к новому или старому JMM, важен потому, что одна из вещей, добавленных JSR-133, заключалась в том, чтобы сделать явные события Thread.start() и Thread.join() - перед границами. –

+0

Определенно новая модель памяти - это действительно точка книги. Thread.start() действительно представляет собой границу, существовавшую раньше, но только в том смысле, что код перед вызовом Thread.start() (т.е. не обязательно сохраняя ВСЕ состояние объекта, установленного во время конструктора) происходит до запуска потока (). –

-3
  1. Ваш класс должен, вероятно, реализует Runnable

  2. Да, ваш код OK

  3. Не нужно расширять Object. Я уверен, что вы знаете, что Object расширяется неявно всеми классами. Я не знаю, откуда эта популярная практика расширять объект, но это плохой стиль.

+1

Re 2. Что ????? –

+0

@ Roman: (1) Да - oops; (2) Я так думаю; (3) Плохой стиль только для IYO, на который вы имеете право (и мнение, которое, я уверен, многие разделили бы - я не только, но мой стиль программирования не является предметом этой публикации). –

3

JLS: Threads and Locks говорит

Два действия могут быть упорядочены происходит, до отношений. Если происходит одно действие - перед другим, то первое видно и упорядочивается до второго.

Если у нас есть два действия x и y, мы пишем hb (x, y), чтобы указать, что x происходит до y.

  • Если x и y являются действиями одного и того же потока, а x - до y в порядке выполнения программы, то hb (x, y).

и

Вызов, чтобы начать() в потоке происходит-до каких-либо действий в запущенном потоке.

, поэтому мы можем заключить, что все действия в конструкторе перед вызовом Thread.start() происходят до всех действий в потоке, который был запущен. Если Thread.start() после записи в поля объекта, то начальный поток увидит все записи в полях объекта. Если в полях нет другой записи (этот другой поток будет прочитан), тогда код будет потокобезопасным.

+0

Да, я читал его так же, но, похоже, он нарушает предостережение о том, что нельзя оставлять ссылку на «этот» побег во время строительства (в нескольких случаях, когда я это сделал, я думаю, что все в порядке, и я не делайте этого часто). В этом случае я ищу подтверждение правильности резьбонарезания, а не хорошего или плохого стиля - первый - это высокий приоритет для исправления, последний - нет. –

+2

Я думаю, что тот факт, что вы объявляете окончательный класс, означает, что это сработает. Однако, если вы (или кто-либо еще) удаляете эту заключительную декларацию, вы рискуете небезопасной публикацией своих объектов. Я бы реорганизовал в принципе, а не по необходимости. –

Смежные вопросы