为什么一旦线程启动,它就永远不能在java中再次启动

时间:2014-12-27 23:36:55

标签: java multithreading

我知道"一旦线程启动,它就永远不会再次启动"。
但我想知道为什么?
如果允许在其他时间稍后重新开始,那该怎么办?
为什么,唯一可以启动线程的时候是它处于NEW状态?至少为什么它也不能在DEAD之后呢?

public class ThreadDemo {

    public static void main(String[] args) {

        Thread thread = new Thread(new MyRunnable());
        thread.start();
        thread.start(); // java.lang.IllegalThreadStateException

    }

}

class MyRunnable implements Runnable{

    @Override
    public void run() {

        System.out.println("run().Thread.currentThread().getName() : " + Thread.currentThread().getName());

    }
}

注意:我已经完成了这些posts。但我的问题更加具体和具有描述性。

请注意,我想了解这一点主要是为了理解线程内部功能以及GC等相关方面如何与线程状态协同工作。

3 个答案:

答案 0 :(得分:5)

因为Thread实现不允许它。您始终可以使用Thread

创建另一个Runnable实例
new Thread(new MyRunnable()).start();
new Thread(new MyRunnable()).start();

修改

JLS-17.4.3. Programs and Program Order说(部分),

  

如果所有操作都以与程序顺序一致的总顺序(执行顺序)发生,则一组操作顺序一致,此外,变量v的每个读取r都将写入写入的值v视为v这样:

w comes before r in the execution order, and

there is no other write w' such that w comes before w' and w' 
comes before r in the execution order.
     

顺序一致性是对程序执行中的可见性和排序的有力保证。在顺序一致的执行中,所有单个操作(例如读取和写入)的总顺序与程序的顺序一致,并且每个单独的操作都是原子的,并且对于每个线程都是立即可见的。

如果Thread个实例可以重新开始,则可能无法实现顺序一致性。

答案 1 :(得分:3)

我可以给你一些观点。

  1. 垃圾收集:活动线程被视为垃圾收集的根。 意味着如果某个对象可以从活动线程到达那么它就不能被垃圾收集 现在,如果线程处于非活动状态(死机),则任何gc算法都将确定如果该线程只能访问的特定对象现在符合垃圾收集的条件,那么它将收集 那个对象。但是现在如果你再次让你的线程活动它是一个问题,因为它的堆栈会有一个损坏的视图,因为许多对象可能已经被垃圾收集了。 你的整个计划都会变得混乱。

    要解决此问题,您将维护另一个状态,让我们将其称为GC_COLLECTABLE。 这意味着线程处于活动状态意味着它的状态!= GC_COLLECTABLE。现在问题出现了 程序员设置此状态时的广告方式。它的类似问题是无法停止线程或确定某些对象何时必须进行垃圾回收。它本身就是一个非常复杂的问题需要解决。解决此问题的最简单方法是,一旦线程无效或无法重新启动它就不会激活它。

  2. thread.join():正如Eliott建议的那样。假设有三个线程T1 T2和一个可重启的线程RST。现在假设T1和T2都调用RST.join()。 T1和T2退出连接时出现问题。如果可以再次启动线程,则join方法是无限的。如果你允许join在DEAD状态之后返回,那么就会有种族和未确定的行为导致T1完成join()但是T2仍然会延迟,因为RST已经重新启动。

  3. API更改:您如何建议应该实施此项目。

    Thread.start(); //这种方法是否足够可能不是

    如果我打电话会怎么样 Thread.start()在循环中 - >它是否会在以前的线程中创建新线程 没有死或将重新启动死线程(听起来像Executors.newCachedThreadPools

    - >如果当前线程没有死或重启线程,则抛出异常   这不是一个非常好的方式使您的程序更具有非确定性。因为同一个线程可能会花费不同的时间用于同一任务,因此您将无法预测程序输出。  至少你需要从start方法对已检查的异常进行签名更改,或者定义一个新的方法重启,但它会遇到与上面相同的问题。

  4. 从我的角度来看,第一点足够单独从重新启动线程。 此外,您可以通过使用ThreadPools获得类似的功能,其中线程可以重用于不同/类似的任务

答案 2 :(得分:0)

如果查看线程的生命周期,则会创建该线程,然后将其移动到等待执行的线程池中

一旦完成它的工作,它就会被调度程序破坏,因此系统不再知道它(空引用) - 因此它不能再次启动,因为Elliot建议创建一个新的Thread是完全正常的MyRunnable函数的工作,但命名线程执行对其范围是唯一的