【问题标题】:Why does an IllegalThreadStateException occur when Thread.start is called again为什么再次调用 Thread.start 时会出现 IllegalThreadStateException
【发布时间】:2011-11-09 22:59:04
【问题描述】:
public class SieveGenerator{

static int N = 50;
public static void main(String args[]){

    int cores = Runtime.getRuntime().availableProcessors();

    int f[] = new int[N];

    //fill array with 0,1,2...f.length
    for(int j=0;j<f.length;j++){
        f[j]=j;
    }

    f[0]=0;f[1]=0;//eliminate these cases

    int p=2;

    removeNonPrime []t = new removeNonPrime[cores];

    for(int i = 0; i < cores; i++){
        t[i] = new removeNonPrime(f,p);
    }

    while(p <= (int)(Math.sqrt(N))){
        t[p%cores].start();//problem here because you cannot start a thread which has already started(IllegalThreadStateException)
        try{
            t[p%cores].join();
        }catch(Exception e){}
        //get the next prime
        p++;
        while(p<=(int)(Math.sqrt(N))&&f[p]==0)p++;
    }


    //count primes
    int total = 0;
    System.out.println();

    for(int j=0; j<f.length;j++){
        if(f[j]!=0){
            total++;
        }
    }
    System.out.printf("Number of primes up to %d = %d",f.length,total);
}
}


class removeNonPrime extends Thread{
int k;
int arr[];

public removeNonPrime(int arr[], int k){
    this.arr = arr;
    this.k = k;
}

public void run(){
    int j = k*k;
    while(j<arr.length){
        if(arr[j]%k == 0)arr[j]=0;
        j=j+arr[k];

    }
}
}

您好,我在运行代码时收到IllegalThreadStateException,我认为这是因为我正在尝试启动一个已经启动的线程。那我怎么杀 还是每次都停止线程,来解决这个问题?

【问题讨论】:

    标签: java multithreading


    【解决方案1】:

    我怎样才能每次都杀死或停止线程来解决这个问题?

    答案是,你不能。一旦启动,Thread 可能不会重新启动。这在javadoc for Thread 中有明确记录。相反,您真正想要做的是 new 一个 RemoveNonPrime 的实例,每次您在循环中出现。

    您的代码中还有一些其他问题。 首先,您需要在再次使用之前增加p

    for(int i = 0; i < cores; i++){
        t[i] = new removeNonPrime(f,p); //<--- BUG, always using p=2 means only multiples of 2 are cleared
    }
    

    其次,你可能是多线程的,但你不是并发的。您拥有的代码基本上一次只允许一个线程运行:

    while(p <= (int)(Math.sqrt(N))){
        t[p%cores].start();//
        try{
            t[p%cores].join(); //<--- BUG, only the thread which was just started can be running now
        }catch(Exception e){}
        //get the next prime
        p++;
        while(p<=(int)(Math.sqrt(N))&&f[p]==0)p++;
    }
    

    只是我的 0.02 美元,但您尝试执行的操作可能有效,但选择下一个最小素数的逻辑并不总是会选择一个素数,例如,如果其他线程之一尚未处理数组的该部分还没有。

    这是一种使用 ExecutorService 的方法,您必须填写一些空白 (...):

    /* A queue to trick the executor into blocking until a Thread is available when offer is called */
    public class SpecialSyncQueue<E> extends SynchronousQueue<E> {
        @Override
        public boolean offer(E e) {
            try {
                put(e);
                return true;
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
                return false;
            }
        }
    }
    
    ExecutorService executor = new ThreadPoolExecutor(cores, cores, new SpecialSyncQueue(), ...);
    void pruneNonPrimes() {
        //...
        while(p <= (int)(Math.sqrt(N))) {
            executor.execute(new RemoveNonPrime(f, p));
            //get the next prime
            p++;
            while(p<=(int)(Math.sqrt(N))&&f[p]==0)p++;
        }
    
    
        //count primes
        int total = 0;
        System.out.println();
    
        for(int j=0; j<f.length;j++){
            if(f[j]!=0){
                total++;
            }
        }
        System.out.printf("Number of primes up to %d = %d",f.length,total);
    }
    
    
    
    class RemoveNonPrime extends Runnable {
        int k;
        int arr[];
    
        public RemoveNonPrime(int arr[], int k){
            this.arr = arr;
            this.k = k;
        }
    
        public void run(){
            int j = k*k;
            while(j<arr.length){
                if(arr[j]%k == 0)arr[j]=0;
                j+=k;
            }
        }
    }
    

    【讨论】:

    • 我希望线程的每个实例都由垃圾收集或清理临时创建的对象的任何东西处理。或者,线程及其创建的对象是否会自行“死亡”……猫有 9 条生命,而线程只有 1 条?
    • 一旦启动,Thread 将执行其run 方法。当run 返回时,线程的执行终止并释放资源。任何只能通过该线程访问的对象最终都将被标记为垃圾回收。但是,是的,Thread 只有一个生命。 ExecutorService 通过BlockingQueueThread 提供更多任务作为Runnable 来解决此问题。
    【解决方案2】:

    您可以改为实现 Runnable 并使用 new Thread( $Runnable here ).start() 或使用 ExecutorService 来重用线程。

    【讨论】:

    • 使用 Runnable 会不会给我同样的问题,或者当我启动线程时,它会在完成后自动停止吗?
    • 否;使用 new Thread( Runnable ).start() 创建并启动一个新线程。可运行对象可以多次使用。是的,如果完成它会停止。如果你创建了很多线程,使用执行器服务可能会更好。
    【解决方案3】:
    * It is never legal to start a thread more than once.
    * In particular, a thread may not be restarted once it has completed
    * execution.
    *
    * @exception IllegalThreadStateException  if the thread was already started
    */
    public synchronized void start() {
    

    在 Android 中,文档仍然提到我们会得到IllegalThreadStateException if the thread was already started
    但是对于某些设备,它不会抛出此异常(在 Kyocera 7.0 上测试)。在三星,HTC等一些流行的设备中,它会正常抛出异常

    我在这里回答是因为 Android 问题被标记为与此问题重复。

    【讨论】:

      【解决方案4】:

      为什么 Thread.start 时会出现 IllegalThreadStateException 再次调用

      因为 JDK/JVM 实现者以这种方式编写了 Thread.start() 方法。在线程完成执行后能够重新启动线程是一个合理的功能期望,这就是 chrisbunney 的答案中所建议的(我已经在该答案中发表了评论)但是如果您查看 Thread.start() 实现,第一行是,

      if (threadStatus != 0)
                  throw new IllegalThreadStateException();
      

      其中threadStatus == 0 表示NEW 状态,所以我的猜测是在执行完成后实现不会将此状态重置为零并且线程留在TERMINATED 状态(非零状态)。因此,当您在同一 Runnable 上创建新的 Thread 实例时,您基本上将此状态重置为零。

      此外,我注意到在同一段落中使用了单词 - ma​​ynever,因为 Phan Van Linh 在某些操作系统上指出了不同的行为,

      多次启动一个线程是不合法的。特别是,一个 线程一旦完成执行就可能不会重新启动。

      我猜他们在上面的 Javadoc 中试图说什么,即使你在某些操作系统上没有得到 IllegalThreadStateException,它在 Java/Thread 类方式中也是不合法的,你可能会得到意想不到的行为。

      著名的线程状态图描绘了相同的场景 - 不会从死状态返回到新状态。

      【讨论】:

        【解决方案5】:

        ThreadPools 可用于传递任务以设置线程数。启动时设置线程数。然后为池添加任务。并且之后您可以阻止,直到所有任务都完成处理。 Here 是一些示例代码。

        【讨论】:

          【解决方案6】:

          我完全不确定我是否理解这个问题。所有从其他线程执行的停止线程的方法都已弃用;停止线程的方法是让它检查一个它和另一个线程可以访问的变量(可能是一个 volatile 变量),并让正在运行的线程偶尔检查它,看看它是否应该退出拥有。

          我不知道您为什么/是否要消除正在运行的线程并使用另一个线程,而且我看不出不同的线程将如何帮助您更快地执行您的总体目标。但有可能我只是不懂数学。

          【讨论】:

            【解决方案7】:

            Thread.isAlive() 方法可以告诉您线程是否已经启动。只需在您想开始线程的地方执行此操作:

               if(!t[p%cores].isAlive()){
                   t[p%cores].start();
               }
            

            【讨论】:

            • Phan Van Linh 的回答中的 Javadoc 清楚地表明 - 线程一旦完成执行就可能不会重新启动。我只是对 may 这个词的用法感到困惑。不确定它在某些情况下是否允许,在某些情况下是否允许。如果您尝试在 if(!thread.isAlive()) 之后启动线程 ..如果之前启动它会导致 IllegalThreadStateException
            • 他的回答确实这么说。请注意,我的回答比他大 6.5 岁,在发表此评论时大约 8.5 岁,所以我不知道我的回答是指与他相同的 Java 版本还是我的回答仍然相关(我有一段时间没有写 Java)。我也不知道当世界围绕它们移动时,像这样的历史性答案会发生什么
            • 至于您关于“可能不”的问题,我将其解读为“不允许”。例如,问题:“我可以在线程启动后重新启动它吗?”,回答:“不,您不能”
            • 感谢您的意见,我了解版本和历史困境。我检查了 Java 1.1.8 文档,其中提到了 start 方法 - 如果线程已经启动,则抛出:IllegalThreadStateException。 方法签名和 Javadoc 与今天,但围绕线程重启的功能似乎是不变的。我猜,他们只是在文档中添加了这一行以提高清晰度 - 多次启动线程是不合法的。特别是,线程一旦完成执行就可能不会重新启动。.
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-10-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多