Законно ли дважды вызывать метод start в одном и том же потоке?

90

Следующий код приводит к тому, java.lang.IllegalThreadStateException: Thread already startedчто я вызвал start()метод второй раз в программе.

updateUI.join();    

if (!updateUI.isAlive()) 
    updateUI.start();

Такое бывает во второй раз updateUI.start()называется. Я прошел через это несколько раз, и поток вызывается и полностью выполняется до завершения, прежде чем попасть updateUI.start().

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

Можно ли запустить поток только один раз? Если да, то что мне делать, если я хочу снова запустить поток? Этот конкретный поток выполняет некоторые вычисления в фоновом режиме, если я не делаю этого в потоке, чем это делается в потоке пользовательского интерфейса, и у пользователя неоправданно долгое ожидание.

Будет
источник
9
Почему вы просто не прочитали javadoc - он четко описывает контракт.
М.П.

Ответы:

112

Из спецификации Java API для Thread.startметода:

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

Более того:

Выдает:
IllegalThreadStateException- если поток уже был запущен.

Так что да, Threadможно запустить только один раз.

Если да, то что мне делать, если я хочу снова запустить поток?

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

Coobird
источник
Благодарю. Я проверил документацию с IDE и учебник по Java для потоков (и Google тоже). Я проверю спецификацию API в будущем. Этого критического «... никогда не разрешено начинать более одного раза ...» нет в других прочтениях.
Уилл
@coobird, если я назначу старое имя объекта потока новому потоку (), после того, как старый поток будет завершен, будет ли старый поток собираться мусором (т.е. он автоматически перерабатывается, или это должно быть сделано явно)?
snapfractalpop
Он будет собираться мусором, пока поток больше не работает.
вылет
1
Этот ответ немного устарел. Если современной программе на Java требуется выполнить задачу более одного раза, она не должна Threadкаждый раз создавать новую . Вместо этого он должен отправить задачу в пул потоков (например, java.util.concurrent.ThreadPoolExecutor)
Соломон Слоу,
13

Абсолютно верно. Из документации :

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

С точки зрения того, что вы можете сделать для повторяющихся вычислений, кажется, что вы могли бы использовать метод SwingUtilities invokeLater . Вы уже экспериментируете с run()прямым вызовом , что означает, что вы уже думаете об использовании, Runnableа не raw Thread. Попробуйте использовать этот invokeLaterметод только наRunnable задаче и посмотрите, подходит ли он немного больше вашему образу мышления.

Вот пример из документации:

 Runnable doHelloWorld = new Runnable() {
     public void run() {
         // Put your UI update computations in here.
         // BTW - remember to restrict Swing calls to the AWT Event thread.
         System.out.println("Hello World on " + Thread.currentThread());
     }
 };

 SwingUtilities.invokeLater(doHelloWorld);
 System.out.println("This might well be displayed before the other message.");

Если вы замените этот printlnвызов своим вычислением, это может быть именно то, что вам нужно.

РЕДАКТИРОВАТЬ: следуя комментарию, я не заметил тега Android в исходном сообщении. Эквивалент invokeLater в работе Android - Handler.post(Runnable). Из его javadoc:

/**
 * Causes the Runnable r to be added to the message queue.
 * The runnable will be run on the thread to which this handler is
 * attached.
 *
 * @param r The Runnable that will be executed.
 *
 * @return Returns true if the Runnable was successfully placed in to the
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.
 */

Итак, в мире Android вы можете использовать тот же пример, что и выше, заменив Swingutilities.invokeLaterсоответствующий пост на Handler.

Боб Кросс
источник
OP спрашивает о потоковой передаче на Android, которая не включает SwingUtilities.
Остин Махони,
@ Остин, ты права. Я добавил примечания к Handler.post (), чтобы проиллюстрировать параллельный код Android.
Боб Кросс,
1
Другой способ, если вы просто пытаетесь обновить свой пользовательский интерфейс, - использовать RunOnUIThread(Runnable)или View.post(Runnable)вместо создания собственного обработчика. Они запустят runnable в основном потоке, что позволит вам обновить пользовательский интерфейс.
Остин Махони 05
3

Только что полученный ответ объясняет, почему вам не следует делать то, что вы делаете. Вот несколько вариантов решения вашей актуальной проблемы.

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

Скиньте собственный поток и пользуйтесь AsyncTask.

Или создайте новую ветку, когда вам это нужно.

Или настройте свой поток для работы вне очереди работ (например, LinkedBlockingQueue), а не перезапускать поток.

CommonsWare
источник
3

Нет , мы не можем снова запустить Thread, это вызовет исключение runtimeException java.lang.IllegalThreadStateException. >

Причина в том, что когда метод run () выполняется потоком, он переходит в мертвое состояние.

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

public class MyClass implements Runnable{

    @Override
    public void run() {
           System.out.println("in run() method, method completed.");
    }

    public static void main(String[] args) {
                  MyClass obj=new MyClass();            
        Thread thread1=new Thread(obj,"Thread-1");
        thread1.start();
        thread1.start(); //will throw java.lang.IllegalThreadStateException at runtime
    }

}

/ * ВЫВОД в методе run (), метод завершен. Исключение в потоке "main" java.lang.IllegalThreadStateException в java.lang.Thread.start (Неизвестный источник) * /

Проверь это

Самир Кази
источник
2

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

Питер Лоури
источник
любой снайпер, как завернуть?
Vinay
1

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

Прямо из уст: Java API Spec

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

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

alanlcode
источник
0

Повторное использование потока является незаконным действием в Java API. Однако вы можете превратить его в работоспособный инструмент и снова запустить этот экземпляр.

Аарон Хе
источник
0

Да, мы не можем запустить уже работающий поток. Он вызовет исключение IllegalThreadStateException во время выполнения - если поток уже был запущен.

Что, если вам действительно нужно запустить поток: Вариант 1) Если поток нужно запускать более одного раза, то нужно создать новый экземпляр потока и вызвать на нем start.

Riteeka
источник
0

Можно ли запустить поток только один раз?

Да. Вы можете запустить его ровно один раз.

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

Не запускайте Threadснова. Вместо этого создайте Runnable и опубликуйте его в Handler of HandlerThread . Вы можете отправить несколько Runnableобъектов. Если хотите отправить данные обратно в поток пользовательского интерфейса, с-в вашем Runnable run()методе, оставить Messageна Handlerнитки UI и процессhandleMessage

Обратитесь к этому сообщению для примера кода:

Android: тост в ветке

Равиндра бабу
источник
0

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

Я знаю, что он больше не запускает тему, но, возможно, это пригодится вам.

public void run() {

    LifeCycleComponent lifeCycleComponent = new LifeCycleComponent();

    try {
        NetworkState firstState = lifeCycleComponent.getCurrentNetworkState();
        Thread.sleep(5000);
        if (firstState != lifeCycleComponent.getCurrentNetworkState()) {
            System.out.println("{There was a NetworkState change!}");
            run();
        } else {
            run();
        }
    } catch (SocketException | InterruptedException e) {
        e.printStackTrace();
    }
}

public static void main(String[] args) {
    Thread checkingNetworkStates = new Thread(new LifeCycleComponent());
    checkingNetworkStates.start();
}

Надеюсь, это поможет, даже если это совсем немного.

Ура

Диоркула
источник
-1

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

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

Торбен
источник
Но предлагалось не вызывать run()напрямую, а просто встроить Runnable в поток и предположительно вызвать start().
H2ONaCl
@ H2ONaCl Если вы читали процитированный мною текст, было предложено обернуть поток в поток. Возможно, вам не удалось прочитать исходное предложение до того, как оно было отредактировано.
Torben