我知道无法重新启动已使用的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
答案 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线程遵循基于下面的状态图的生命周期。一旦线程处于最终状态,它就结束了。这只是设计
答案 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();
}
}
}