Начиная поток как последнее утверждение конструктора конечного класса - PullRequest
3 голосов
/ 29 марта 2012

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

public final class Test {

    private final int value;

    public Test(int value) throws InterruptedException {
        start();
        this.value = value;
    }

    private void start() throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    System.out.println("Construction OK = " + Boolean.toString(Test.this.value == 5));
                }
            }).start();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Test test = new Test(5);
    }
}

Это печатает (очевидно, не то же самое при каждом запуске):

Конструкция OK = false
Конструкция OK = false
Конструкция OK = false
Конструкция OK = false
Конструкция OK = false
Конструкция OK = false
Конструкция OK = false
Конструкция OK = true
Конструкция OK = true
Конструкция OK = true

Now IF метод start является последним оператором конструктора AND Переупорядочение предотвращается с помощью синхронизированного блока вокруг инициализации конечного значения,все еще существует риск, связанный с запуском потоков из конструктора?

public Test(int value) throws InterruptedException {
    synchronized (new Object()) { // to prevent reordering + no deadlock risk
        this.value = value;
    }
    start();
}

EDIT
Я не думаю, что это было задано ранее в том смысле, что вопрос более конкретенчем просто «Могу ли я запустить потоки в конструкторе»: потоки запускаются в последнем утверждении конструктора, что означает, что строительство объекта завершено (насколько я понимаю).

Ответы [ 4 ]

4 голосов
/ 29 марта 2012

Да, потому что Test может быть разделено на подклассы, и тогда start() будет выполнено до создания экземпляра. У конструктора подклассов может быть что-то еще.

Так что класс должен быть final как минимум.

1 голос
/ 29 марта 2012

В этом конкретном случае я хотел бы отметить value как volatile (или использовать AtomicBoolean) и запускать потоки после установки значения:

this.value = value;   // this.value.set(value)  if using AtomicBoolean
start();

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


Относительно вашего редактирования:

[...], что означает, что строительство объекта завершено (насколько я понимаю).

Это верно, но рассмотрим следующий сценарий:

Ваши тестовые потоки немного сложнее и получают доступ к списку testList тестов.Теперь, если вы выполните

testList.add(new Test());

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

Test t = new Test();
testList.add(t);
t.start();

Связанный вопрос:

0 голосов
/ 06 февраля 2013

synchronized(new Object()) НЕ препятствует переупорядочению - поскольку монитор является локальной переменной, компилятор фактически может игнорировать синхронизированный блок .

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

0 голосов
/ 29 марта 2012

В конструкторе вы вызываете метод start с помощью

start()

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

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