Poniższy kod prowadzi do java.lang.IllegalThreadStateException: Thread already started
sytuacji, gdy wywołałem start()
metodę po raz drugi w programie.
updateUI.join();
if (!updateUI.isAlive())
updateUI.start();
Dzieje się to za drugim razem updateUI.start()
. Przechodziłem przez to wiele razy, a wątek jest wywoływany i całkowicie biegnie do końca przed uderzeniem updateUI.start()
.
Wywołanie updateUI.run()
pozwala uniknąć błędu, ale powoduje, że wątek działa w wątku interfejsu użytkownika (wątku wywołującym, jak wspomniano w innych postach na SO), co nie jest tym, czego chcę.
Czy wątek można uruchomić tylko raz? Jeśli tak, to co mam zrobić, jeśli chcę ponownie uruchomić wątek? Ten konkretny wątek wykonuje obliczenia w tle, jeśli nie robię tego w wątku, to jest to robione w wątku interfejsu użytkownika, a użytkownik ma nieuzasadnione długie oczekiwanie.
źródło
Odpowiedzi:
Ze specyfikacji interfejsu API języka Java dla
Thread.start
metody:Ponadto:
Więc tak, a
Thread
można uruchomić tylko raz.Jeśli a
Thread
trzeba uruchomić więcej niż jeden raz, należy utworzyć nową instancjęThread
i wywołaćstart
ją.źródło
Thread
każdym razem tworzyć nowego . Zamiast tego powinien przesłać zadanie do puli wątków (np.java.util.concurrent.ThreadPoolExecutor
)Dokładnie tak. Z dokumentacji :
Jeśli chodzi o to, co możesz zrobić dla powtarzających się obliczeń, wydaje się, że możesz użyć metody InvokeLater SwingUtilities . Już eksperymentujesz z dzwonieniem
run()
bezpośrednio, co oznacza, że już myślisz o użyciuRunnable
raczej niż surowegoThread
. Spróbuj zastosować tęinvokeLater
metodę tylko doRunnable
zadania i zobacz, czy lepiej pasuje to do Twojego schematu myślowego.Oto przykład z dokumentacji:
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.");
Jeśli zastąpisz to
println
wywołanie swoimi obliczeniami, może to być dokładnie to, czego potrzebujesz.EDYCJA: podążając za komentarzem, nie zauważyłem tagu Androida w oryginalnym poście. Odpowiednikiem invokeLater w pracy systemu Android jest
Handler.post(Runnable)
. Z jego 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. */
Tak więc w świecie Androida możesz użyć tego samego przykładu, co powyżej, zastępując
Swingutilities.invokeLater
odpowiedni post naHandler
.źródło
RunOnUIThread(Runnable)
lubView.post(Runnable)
zamiast tworzenia własnego programu obsługi. Będą one uruchamiać plik runnable w głównym wątku, umożliwiając aktualizację interfejsu użytkownika.Właśnie otrzymana odpowiedź wyjaśnia, dlaczego nie powinieneś robić tego, co robisz. Oto kilka opcji rozwiązania rzeczywistego problemu.
Zrzuć swój własny wątek i użyj
AsyncTask
.Lub utwórz nowy wątek, kiedy go potrzebujesz.
Lub skonfiguruj swój wątek tak, aby działał poza kolejką roboczą (np.
LinkedBlockingQueue
), Zamiast restartować wątek.źródło
Nie , nie możemy ponownie uruchomić Thread, spowoduje to wystąpienie wyjątku runtimeException java.lang.IllegalThreadStateException. >
Weźmy przykład - myślenie o ponownym uruchomieniu wątku i wywołaniu na nim metody start () (która wewnętrznie wywoła metodę run ()) jest dla nas czymś, co przypomina proszenie martwego człowieka o obudzenie się i uruchomienie. Ponieważ po zakończeniu życia osoba przechodzi w stan martwy.
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 } }
Sprawdź to
źródło
Co powinieneś zrobić, to stworzyć Runnable i owijać go nowym wątkiem za każdym razem, gdy chcesz uruchomić Runnable. Byłoby to naprawdę brzydkie, ale możesz owinąć wątek innym wątkiem, aby ponownie uruchomić kod, ale zrób to tylko wtedy, gdy naprawdę musisz.
źródło
Jak powiedziałeś, wątku nie można rozpocząć więcej niż jeden raz.
Prosto z paszczy konia: Java API Spec
Jeśli chcesz ponownie uruchomić wszystko, co dzieje się w twoim wątku, będziesz musiał utworzyć nowy wątek i uruchomić go.
źródło
Ponowne użycie wątku jest nielegalną czynnością w Java API. Możesz jednak umieścić go w działającym narzędziu i ponownie uruchomić tę instancję.
źródło
Tak, nie możemy już uruchomić wątku. Wyrzuci wyjątek IllegalThreadStateException w czasie wykonywania - jeśli wątek został już uruchomiony.
Co jeśli naprawdę potrzebujesz uruchomić wątek: Opcja 1) Jeśli wątek musi być uruchomiony więcej niż raz, należy utworzyć nową instancję wątku i wywołać start na nim.
źródło
Tak. Możesz zacząć to dokładnie raz.
Nie uruchamiaj
Thread
ponownie. Zamiast tego utwórz Runnable i umieść go w Handler of HandlerThread . Możesz przesłać wieleRunnable
obiektów. Jeśli chcesz wysłać dane z powrotem do wątku UI, ze w swojejRunnable
run()
metodzie, umieścićMessage
naHandler
z wątku UI i proceshandleMessage
Zobacz przykładowy kod w tym poście:
Android: Toast w wątku
źródło
Nie wiem, czy jest to dobra praktyka, ale kiedy pozwolę wywołać run () wewnątrz metody run (), nie zgłasza błędu i faktycznie robi dokładnie to, co chciałem.
Wiem, że to nie jest ponowne rozpoczęcie wątku, ale może ci się to przyda.
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(); }
Mam nadzieję, że to pomoże, nawet jeśli to tylko trochę.
Twoje zdrowie
źródło
Musiałem naprawić wyciek zasobów, który został spowodowany przez programistę, który utworzył wątek, ale zamiast go uruchomić, bezpośrednio wywołał metodę run (). Unikaj więc tego, chyba że naprawdę wiesz, jakie skutki uboczne wywołuje.
źródło
run()
bezpośrednim wywołaniu , tylko po to, aby osadzić Runnable w wątku i prawdopodobnie wywołaćstart()
.