代码之家  ›  专栏  ›  技术社区  ›  Curd

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

  •  14
  • Curd  · 技术社区  · 14 年前

    我知道不可能重新启动一个使用的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
    
    9 回复  |  直到 14 年前
        1
  •  18
  •   luis.espinal    14 年前

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

    我的客户设想是,线程可以直接(为了效率或其他限制)与实际 本地的 在某些操作系统中可以重新启动的资源,但在其他操作系统中不能重新启动。如果Java语言的设计者允许重新启动线程,它们可能会限制JVM可以运行的操作系统的数量。

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

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

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

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

        2
  •  15
  •   Andrzej Doyle    14 年前

    我会反过来提出这个问题-为什么 应该 线程对象是否可重新启动?

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

    所以除非你能想出一个具体的原因,为什么要重新启动一个给定的 Thread 比用同样的方法创建一个新的更好的选择 Runnable 我认为设计决策是为了更好。

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

        3
  •  2
  •   Mark Peters    14 年前

    因为他们不是这样设计的。从清晰的角度来看,这对我来说是有意义的。线程表示执行的线程,而不是任务。当执行的线程完成后,它就完成了它的工作,如果重新从顶部开始的话,就会使事情变得混乱。

    另一方面,runnable代表一个任务,可以提交到多个线程中任意多次。

        4
  •  1
  •   Paul Tomblin    14 年前

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

        5
  •  1
  •   akf    14 年前

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

    我在下面。一旦线程处于最终状态,它就结束了。这就是简单的设计。
    alt text

        6
  •  0
  •   Jonathan    14 年前

    你可以通过使用 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
  •  0
  •   gfelisberto    14 年前

    如果您关心创建一个新线程对象的开销,那么您可以使用执行器。

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

    运行这个,您将看到相同的线程被用于所有可运行的对象。

        8
  •  0
  •   Solomon Slow    9 年前

    Thread 不是一个 线 . 一 线 是代码的执行。一 螺纹 是程序用来创建和管理 线 .

    假设你喜欢打网球。假设你和你的朋友玩了一套非常棒的游戏。如果你说“太不可思议了,让我们再玩一次”,你的朋友会怎么反应呢?你可能会认为你疯了。再谈一次同一盘也没有意义。如果你再玩一次,你就是在玩 不同的 集合。

    线 是代码的执行。甚至谈论“重新使用”一条执行线也没有意义,因为在网球比赛中谈论重新使用同一盘也没有意义。即使代码的另一次执行以相同的顺序执行所有相同的语句,它仍然是 不同的 执行。

    安德泽·道尔问道:“你为什么要 希望 重新使用 螺纹 “为什么呢?如果A 螺纹 对象表示一个执行线程——一个你甚至不能谈论重新使用的短暂的东西——那么你为什么想要或期望 螺纹 对象 可重复使用?

        9
  •  -1
  •   Nate    10 年前

    我一直在寻找和你一样的解决方案,我是这样解决的。如果发生mousepresed事件,您可以终止它,也可以重用它,但它需要初始化,如下所示。

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