为什么不能重新启动Java Thread对象?

时间:2010-10-27 14:12:51

标签: java multithreading

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

我不明白为什么start()(或者至少是restart())方法不能以某种方式将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

9 个答案:

答案 0 :(得分:18)

  

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

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

考虑到这一点,我想不出一个允许线程或进程在完成或终止后重新启动的操作系统。当一个过程完成时,它就会死掉。你想要另一个,你重新启动它。你永远不会复活它。

除了底层操作系统强加的效率和限制问题外,还存在分析和推理问题。当事物是不可变的或具有离散的,有限的生命周期时,你可以推理出并发性。就像状态机一样,它们必须具有终端状态。是开始,等待,完成?如果允许Threads复活,那么这样的事情就不容易被推断。

您还必须考虑复活线程的含义。重新创建它的状态,是否可以安全地复活?你能复活一个异常结束的线程吗?等

太毛茸茸,太复杂了。所有这些都是微不足道的收获。最好将Threads保留为不可复制的资源。

答案 1 :(得分:15)

我反过来提出问题 - 为什么应该一个Thread对象可以重启?

可以说更容易推理(并且可能实现)一个只执行一次给定任务的线程,然后永久完成。要重新启动线程,需要更复杂地查看程序在给定时间所处的状态。

因此,除非您能够提出一个具体原因,为什么重新启动给定的Thread比仅创建具有相同Runnable的新选项更好,我认为设计决策是为了更好。

(这大致类似于关于可变vs final变量的论证 - 我发现最终的“变量”更容易推理,而更愿意创建多个新的常量变量,而不是重用现有变量。)< / p>

答案 2 :(得分:2)

因为他们没有这样设计。从清晰的角度来看,这对我来说很有意义。线程表示执行线程,而不是任务。当那个执行线程完成后,它已经完成了它的工作而且只是混乱的事情是它再次从顶部开始。

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

答案 3 :(得分:1)

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

运行它

答案 4 :(得分:1)

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

答案 5 :(得分:0)

您可以通过使用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();
        }
    }
}

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

答案 6 :(得分:0)

如果您担心创建新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对象使用相同的线程。

答案 7 :(得分:0)

Thread不是主题线程是代码的执行。 Thread是程序用来创建和管理线程的生命周期的对象。

假设你喜欢打网球。假设你和你的朋友玩得非常棒。如果你说,你的朋友会如何反应,&#34;这太不可思议了,让我们再玩一次。&#34;你的朋友可能认为你疯了。即使谈论再次播放相同的剧集也没有意义。如果您再次播放,则会播放不同的设置。

线程是代码的执行。即使谈论&#34;重新使用&#34;也没有意义。一个执行的线程,出于同样的原因,谈论在网球中重新播放相同的一组是没有意义的。即使您的代码的另一次执行以相同的顺序执行所有相同的语句,它仍然是不同的执行。

Andrzej Doyle问道,&#34;为什么你希望重新使用Thread?&#34;为什么呢?如果一个Thread对象代表一个执行的线程 - 一个你甚至不能谈论重新使用的短暂事物 - 那你为什么要或期望Thread 对象可以重复使用吗?

答案 8 :(得分:-1)

我一直在搜索您似乎正在寻找的相同解决方案,我以这种方式解决了它。如果你发生mousePressed事件你可以终止它也重用它,但它需要初始化,如下所示。

class MouseHandler extends MouseAdapter{
    public void mousePressed(MouseEvent e) {            
        if(th.isAlive()){
            th.interrupt();
            th = new Thread();
        }
        else{
            th.start();
        }
    }

}