Чи правомірно викликати метод запуску двічі в одному потоці?


90

Наступний код приводить до того, java.lang.IllegalThreadStateException: Thread already startedколи я вдруге викликав start()метод у програмі.

updateUI.join();    

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

Це трапляється вдруге, коли updateUI.start()називається. Я пройшов через нього кілька разів, і потік викликається і повністю запускається до завершення перед натисканням updateUI.start().

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

Чи можна розпочати нитку лише один раз? Якщо так, то що робити, якщо я хочу запустити ланцюжок знову? Цей конкретний потік робить деякий розрахунок у фоновому режимі, якщо я не роблю це в потоці, ніж це робиться в потоці інтерфейсу користувача, і користувачеві надмірно довго чекати.


9
Чому ви просто не прочитали th javadoc - він чітко описує цей контракт.
мП.

Відповіді:


112

З специфікації Java API для Thread.startметоду:

Ніколи не законно запускати ланцюжок більше одного разу. Зокрема, потік не може бути перезапущений після завершення виконання.

Крім того:

Кидки:
IllegalThreadStateException- якщо нитка вже була розпочата.

Так що так, a Threadможна запустити лише один раз.

Якщо так, то що робити, якщо я хочу запустити ланцюжок знову?

Якщо Threadпотрібно запустити програму більше одного разу, тоді слід створити новий екземпляр Threadі викликати startйого.


Дякую. Я перевірив документацію в IDE та підручнику Java на наявність потоків (і google теж). Я перевірю специфікацію API у майбутньому. Цього критичного ".. ніколи не дозволяється починати більше одного разу .." немає в інших читаннях.
Буде

@coobird, якщо я присвоюю ім'я старого об'єкта потоку новому потоку (), після закінчення старого потоку чи буде зібраний старий потік (тобто він автоматично переробляється, або це потрібно робити явно)?
snapfractalpop

Це буде зібране сміття, поки Потік більше не працює.
літаючий

1
Ця відповідь трохи застаріла. Якщо сучасній програмі Java потрібно виконати завдання кілька разів, тоді вона не повинна створювати Threadщоразу нову . Натомість він повинен подати завдання до пулу потоків (наприклад, java.util.concurrent.ThreadPoolExecutor)
Соломон Повільний

13

Точно правильно. З документації :

Ніколи не законно запускати ланцюжок більше одного разу. Зокрема, потік не може бути перезапущений після завершення виконання.

З точки зору того, що ви можете зробити для повторного обчислення, здається, що ви могли б використовувати метод SwingUtilities invokeLater . Ви вже експериментуєте з run()безпосереднім дзвінком, тобто ви вже думаєте про те, щоб скористатися Runnableне сирим, а сирим 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.
Остін Махоні,

@Austyn, ти маєш рацію. Я додав зауваження щодо Handler.post () для ілюстрації паралельного коду Android.
Bob Cross

1
Іншим способом, якщо ви просто намагаєтесь оновити свій інтерфейс, є використання RunOnUIThread(Runnable)або View.post(Runnable)замість створення власного обробника. Вони запускатимуть основний потік, що дозволяє виконувати оновлення інтерфейсу користувача.
Остін Махоні,

3

Щойно надійшла відповідь охоплює, чому ви не повинні робити те, що робите. Ось кілька варіантів вирішення вашої актуальної проблеми.

Цей конкретний потік робить деякий розрахунок у фоновому режимі, якщо я не роблю це в потоці, ніж це робиться в потоці інтерфейсу користувача, і користувачеві надмірно довго чекати.

Скиньте власну нитку і використовуйте AsyncTask.

Або створіть нову нитку, коли вона вам потрібна.

Або налаштуйте свій потік для роботи з черги роботи (наприклад, LinkedBlockingQueue), а не для перезапуску потоку.


3

Ні , ми не можемо знову запустити Thread, це зробить runtimeException java.lang.IllegalThreadStateException. >

Причиною є те, що метод Thread, коли метод 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 і обернути його новою Thread кожного разу, коли ви хочете запускати Runnable. Це було б дуже потворно робити, але ви можете обернути нитку іншим потоком, щоб запустити код для нього знову, але тільки це потрібно робити насправді.


будь-який сніп, як обернути?
Віней,

1

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

Прямо з пащі коня: Java API Spec

Ніколи не законно запускати ланцюжок більше одного разу. Зокрема, потік не може бути перезапущений після завершення виконання.

Якщо вам потрібно повторно запустити все, що відбувається у вашому потоці, вам доведеться створити новий потік і запустити його.


0

Повторне використання потоку є незаконною дією в Java API. Однак ви можете обернути його у запущений реалізатор і повторно запустити цей екземпляр знову.


0

Так, ми не можемо розпочати вже запущений потік. Він викине IllegalThreadStateException під час виконання - якщо потік вже запущений.

Що робити, якщо вам дійсно потрібно запустити потік: Варіант 1) Якщо Thread потрібно запускати більше одного разу, тоді слід створити новий екземпляр Thread і викликати на ньому start.


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

Це було б дуже потворно робити, але ви можете обернути нитку іншим потоком, щоб запустити код для нього знову, але тільки це потрібно робити насправді.

Мені довелося виправити витік ресурсу, спричинений програмістом, який створив Thread, але замість того, щоб розпочати () його, він безпосередньо викликав метод run (). Тож уникайте цього, якщо ви насправді не знаєте, які побічні ефекти це викликає.


Але пропозиція полягала не в тому, щоб зателефонувати run()безпосередньо, а лише вбудувати Runnable в Thread і, мабуть, викликати start().
H2ONaCl

@ H2ONaCl Якщо ви читаєте текст, який я цитував, пропозиція полягала в тому, щоб обернути нитку в нитку. Можливо, вам не вдалося прочитати оригінальну пропозицію до її редагування.
Торбен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.