有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

多线程为什么不能重新启动Java线程对象?

我知道不可能重新启动一个使用过的Java线程对象,但我找不到一个解释为什么这是不允许的;即使保证线程已经完成(参见下面的示例代码)

我不明白为什么start()(或者至少一个restart())方法不能以某种方式将线程对象的内部状态(无论它们是什么)重置为新创建的线程对象时的相同值

示例代码:

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

共 (6) 个答案

  1. # 1 楼答案

    I know that it is not possible to restart a used Java Thread object, but I don't find an explanation why this is not allowed; even if it is guaranteed that the thread has finished (see example code below).

    我的猜测是线程可能直接绑定到实际的本地资源上(出于效率或其他限制),这些资源可能在某些操作系统中可重新启动,但在其他操作系统中不可重新启动。如果Java语言的设计者允许重新启动线程,他们可能会限制JVM可以运行的操作系统的数量

    想想看,我想不出一个操作系统允许线程或进程在完成或终止后重新启动。当一个进程完成时,它就死了。如果您想要另一个,请重新启动它。你永远不会复活它

    除了底层操作系统强加的效率和限制问题外,还有分析和推理问题。当事物要么是不可变的,要么是离散的、有限的生命周期时,您可以对并发性进行推理。就像状态机一样,它们必须有一个终端状态。它开始了,等待着,结束了吗?如果你允许线程复活,这类事情就不容易解释了

    <>你还必须考虑线程复活的含义。重新创建它的堆栈,它的状态,是否可以安全地恢复?你能复活一条异常终止的线吗?等等

    太多毛,太复杂。所有这些都是为了微不足道的利益。最好将线程保留为不可恢复的资源

  2. # 2 楼答案

    您可以通过使用java.util.concurrent.ThreadPoolExecutor或手动使用一个线程来解决这个问题,该线程在给定的每个Runnable上调用Runnable.run(),而不是在完成时实际退出

    这并不完全是您所要问的,但是如果您担心线程构造时间,那么它可以帮助解决这个问题。下面是手动方法的一些示例代码:

    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();
            }
        }
    }
    

    显然,这只是一个例子。它需要更好的错误处理代码,也许还需要更多的调优

  3. # 3 楼答案

    因为他们不是那样设计的。从清晰的角度来看,这对我来说是有意义的。线程表示执行线程,而不是任务。当执行线程完成时,它已经完成了它的工作,如果它再次从顶部开始,它就会把事情搞得一团糟

    另一方面,Runnable表示一个任务,可以根据需要多次提交给多个线程

  4. # 4 楼答案

    为什么不创建一个新线程?如果您担心创建MyThread对象的开销,请将其设置为可运行,并使用new Thread(myThread).start();运行它

  5. # 5 楼答案

    我会提出另一个问题-为什么线程对象应该是可重启的

    可以说,对一个只执行给定任务一次,然后永久完成的线程进行推理(并可能实现)要容易得多。重新启动线程需要一个更复杂的视图,了解程序在给定时间处于何种状态

    因此,除非你能想出一个具体的理由,说明重新启动一个给定的Thread比仅仅用相同的Runnable创建一个新的Runnable更好,否则我认为设计决策是更好的

    (这与关于可变变量与final变量的争论大致相似——我发现最终的“变量”更容易推理,更愿意创建多个新的常量变量,而不是重用现有变量。)

  6. # 6 楼答案

    Java线程遵循基于以下状态图的生命周期。一旦线程处于最终状态,它就结束了。这就是设计
    alt text