Почему нельзя перезапустить объект Java Thread?
Я знаю, что перезапустить использованный объект Java Thread невозможно, но я не нашел объяснения, почему это не разрешено; даже если гарантировано, что поток завершен (см. пример кода ниже).
Я не понимаю, почему метод start()
(или, по крайней мере, restart()
) не должен каким-либо образом reset внутренними состояниями - независимо от того, что они есть) объекта Thread с теми же значениями, которые они имеют, когда объект Thread только что создан.
Пример кода:
class ThreadExample {
public static void main(String[] args){
Thread myThread = new Thread(){
public void run() {
for(int i=0; i<3; i++) {
try{ sleep(100); }catch(InterruptedException ie){}
System.out.print(i+", ");
}
System.out.println("done.");
}
};
myThread.start();
try{ Thread.sleep(500); }catch(InterruptedException ie){}
System.out.println("Now myThread.run() should be done.");
myThread.start(); // <-- causes java.lang.IllegalThreadStateException
} // main
} // class
Ответы
Ответ 1
Я знаю, что невозможно перезапустите используемый объект Java Thread, но Я не нашел объяснения, почему это не допускается; даже если это что нить имеет (см. пример кода ниже).
Мой гостевой процесс заключается в том, что потоки могут быть напрямую привязаны (для эффективности или других ограничений) к фактическим исходным ресурсам, которые могут быть перезапущены в некоторых операционных системах, но не в других. Если разработчики языка Java разрешили перезапускать Threads, они могут ограничить количество операционных систем, на которых может работать JVM.
Подумайте об этом, я не могу думать о ОС, которая позволяет перезапустить поток или процесс после его завершения или завершения. Когда процесс завершается, он умирает. Вы хотите другой, вы перезапустите его. Вы никогда не воскресите его.
Помимо вопросов эффективности и ограничений, налагаемых базовой ОС, существует проблема анализа и рассуждений. Вы можете рассуждать о concurrency, когда вещи либо неизменяемы, либо имеют дискретный, конечный срок службы. Как и государственные машины, они должны иметь состояние терминала. Это началось, дожидаясь, закончилось? Такие вещи не могут быть легко обоснованы, если вы разрешаете Threads воскресать.
Вы также должны учитывать последствия воскрешения потока. Восстановить свой стек, его состояние, безопасно воскресить? Можете ли вы воскресить нить, которая закончилась ненормально? Etc.
Слишком волосатый, слишком сложный. Все это для незначительных выгод. Лучше сохранить Threads как нересупретируемые ресурсы.
Ответ 2
Я поставил бы вопрос наоборот: почему объект Thread должен быть перезагружен?
Возможно, гораздо проще рассуждать о (и, возможно, реализовать) Thread, который просто выполняет свою заданную задачу ровно один раз и затем окончательно завершается. Для перезапуска потоков потребуется более сложное представление о состоянии, в котором была установлена программа в данный момент времени.
Поэтому, если вы не можете найти конкретную причину, по которой перезапуск данного Thread
является лучшим вариантом, чем просто создание нового с тем же Runnable
, я бы сказал, что дизайнерское решение к лучшему.
(Это в целом похоже на аргумент об изменяемых переменных vs final
. Я считаю, что конечные "переменные" гораздо проще рассуждать и скорее создадут несколько новых постоянных переменных, чем повторное использование существующих.)
Ответ 3
Потому что они так не проектировали. С ясной точки зрения это имеет смысл для меня. Thread представляет поток выполнения, а не задачу. Когда эта последовательность выполнения завершилась, она выполнила свою работу, и она просто путала вещи, чтобы она начиналась сверху.
A Runnable, с другой стороны, представляет собой задачу и может быть передана многим потокам столько раз, сколько вам захочется.
Ответ 4
Почему вы не хотите создавать новую тему? Если вы обеспокоены накладными расходами на создание своего объекта MyThread, сделайте его Runnable и запустите его с помощью new Thread(myThread).start();
Ответ 5
Темы Java следуют жизненному циклу, основанному на приведенной ниже диаграмме состояний. Когда поток находится в конечном состоянии, он закончен. Это просто дизайн.
![alt text]()
Ответ 6
Вы можете обойти это, используя java.util.concurrent.ThreadPoolExecutor
или вручную, имея поток, который вызывает Runnable.run()
на каждом Runnable
, который он задает, а не фактически выходящий, когда он будет завершен.
Это не совсем то, о чем вы спрашивали, но если вы беспокоитесь о времени строительства нити, это может помочь решить эту проблему. Вот пример кода для ручного метода:
public class ReusableThread extends Thread {
private Queue<Runnable> runnables = new LinkedList<Runnable>();
private boolean running;
public void run() {
running = true;
while (running) {
Runnable r;
try {
synchronized (runnables) {
while (runnables.isEmpty()) runnables.wait();
r = runnables.poll();
}
}
catch (InterruptedException ie) {
// Ignore it
}
if (r != null) {
r.run();
}
}
}
public void stopProcessing() {
running = false;
synchronized (runnables) {
runnables.notify();
}
}
public void addTask(Runnable r) {
synchronized (runnables) {
runnables.add(r);
runnables.notify();
}
}
}
Очевидно, это просто пример. Он должен иметь лучший код обработки ошибок и, возможно, больше настроек.
Ответ 7
Если вы обеспокоены накладными расходами на создание нового объекта Thread, вы можете использовать исполнителей.
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class Testes {
public static void main(String[] args) {
Executor executor = Executors.newSingleThreadExecutor();
executor.execute(new Testes.A());
executor.execute(new Testes.A());
executor.execute(new Testes.A());
}
public static class A implements Runnable{
public void run(){
System.out.println(Thread.currentThread().getId());
}
}
}
Запустив это, вы увидите, что тот же поток используется для всех объектов Runnable.
Ответ 8
A Thread
не является нитью. Поток - это выполнение вашего кода. A Thread
- это объект, который ваша программа использует для создания и управления жизненным циклом потока.
Предположим, вам нравится играть в теннис. Предположим, вы и ваш друг играете в действительно потрясающий набор. Как бы ваш друг отреагировал, если бы сказал: "Это было невероятно, пусть снова сыграет". Ваш друг может подумать, что вы с ума сошли. Не имеет смысла даже говорить о том, чтобы снова играть в один и тот же набор. Если вы снова играете, вы играете в другой набор.
Поток - это выполнение вашего кода. Не имеет смысла даже говорить о "повторном использовании" потока исполнения по той же причине, что бессмысленно говорить о повторном воспроизведении того же набора в теннисе. Даже если другое исполнение вашего кода выполняет все те же операторы в том же порядке, это все равно другое выполнение.
Анджей Дойл спросил: "Почему вы хотите повторно использовать Thread
?" Почему? Если объект Thread
представляет поток выполнения - эфемерную вещь, о которой вы даже не можете говорить о повторном использовании, то почему вы хотите или ожидаете, что объект Thread
будет повторно использоваться?
Ответ 9
Я искал то же самое решение, которое вы, похоже, ищете, и я решил его таким образом. если вы обнаружите событие mousePressed, вы можете его закончить и повторно использовать, но его нужно инициализировать, как вы можете видеть ниже.
class MouseHandler extends MouseAdapter{
public void mousePressed(MouseEvent e) {
if(th.isAlive()){
th.interrupt();
th = new Thread();
}
else{
th.start();
}
}
}