Почему run () вызывается не сразу, когда start () вызывается для объекта потока в Java - PullRequest
7 голосов
/ 22 апреля 2010

Или это?
У меня есть объект потока от:

Thread myThread = new Thread(pObject);

Где pObject - это объект класса, реализующего интерфейс Runnable, а затем у меня вызывается метод start для объекта потока, например:

myThread.start();

Теперь я понимаю, что когда вызывается start (), JVM неявно (и немедленно) вызывает метод run (), который может быть переопределен (как в моем случае)

Однако в моем случае оказывается, что метод start () вызывается не сразу (как требуется), а до тех пор, пока другие операторы / методы не будут завершены из вызывающего блока, т.е. если у меня был метод после вызова start () вот так:

myThread.start();
doSomethingElse();

doSomthingElse () выполняется до того, как метод run () будет запущен вообще.
Возможно, я ошибаюсь в первоначальной предпосылке, что run () всегда вызывается сразу после вызова start (). Пожалуйста помоги! Желаемое снова делает выполнение run () сразу после start (). Спасибо.

Ответы [ 6 ]

16 голосов
/ 22 апреля 2010

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

13 голосов
/ 22 апреля 2010

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

Вы правы, полагая, что здесь нет никаких гарантий. Создание предположений о поведении многопоточного кода является источником большой боли - старайтесь не делать этого!

11 голосов
/ 22 апреля 2010

Теперь я понимаю, что когда вызывается start (), JVM неявно (и немедленно) вызывает метод run () ...

Это неверно.Он неявно вызывает run(), но вызов не обязательно происходит немедленно.

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

Чтобы заставить ваш поток начать работу немедленно (или, если быть более точным, запустить до doSomethingElse())вам нужно сделать некоторую явную синхронизацию;например, что-то вроде этого:

    java.util.concurrent.CountDownLatch latch = new CountdownLatch(1);
    new Thread(new MyRunnable(latch)).start();
    latch.await(); // waits until released by the child thread.
    doSomethingElse();

где

class MyRunnable implements Runnable {
    private CountDownLatch latch;
    MyRunnable (CountDownLatch latch) { this.latch = latch; }
    public void run() {
        doSomeStuff();
        latch.countDown(); // releases the parent thread
        doSomeMoreStuff();
    }
    ...
}

Существуют другие способы реализации синхронизации с использованием классов параллелизма или примитивов mutex / wait / notify Java 1 .Но явная синхронизация между двумя потоками - единственный способ гарантировать требуемое поведение.

Обратите внимание, что вызов doSomething() в дочернем потоке завершится до освобождения родительского потока, но мы ничего не можем сказатьо порядке исполнения doSomethingElese() и doSomeMoreStuff().(Один может работать раньше другого и наоборот, или они могут работать параллельно.)


1 - Использование wait / notify не рекомендуется, но это может быть вашимединственный вариант, если API параллелизма недоступны;например на Java ME.

6 голосов
/ 22 апреля 2010

Когда вы вызываете myThread.start(), ваш поток становится доступным для исполнения. Получит ли он на самом деле процессор, и как долго - до планировщика ОС. Фактически, ваш run() может сразу получить контроль, но потерять его, прежде чем он сможет сделать что-то, что вы заметите. Единственный способ убедиться, что ваш поток выполняет то, что вам нужно до doSomethingElse(), - это использовать явную синхронизацию.

3 голосов
/ 22 апреля 2010

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

pObject.run();
doSomethingElse();

или

doSomethingElse();
pObject.run();

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

Еще сложнее, если два или более потоков обращаются к одним и тем же переменным.Значение в одном может никогда не обновляться в одном потоке при определенных обстоятельствах.

Я настоятельно рекомендую:

  1. Вы не сделать вашу программу многопоточной, если вам абсолютно не нужно;и

  2. Если вы это сделаете, купите и прочитайте от обложки до обложки Java-параллелизма на практике Брайана Гетца .

0 голосов
/ 24 октября 2018

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

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