简体   繁体   中英

Why does an IllegalThreadStateException occur when Thread.start is called again

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];

    }
}
}

Hi I'm getting an IllegalThreadStateException when I run my code and I've figured it's because I am trying to start a thread that has already been started. So how could I kill or stop the thread each time, to get around this problem?

how could I kill or stop the thread each time, to get around this problem?

The answer is, you can't. Once started, a Thread may not be restarted. This is clearly documented in the javadoc for Thread . Instead, what you really want to do is new an instance of RemoveNonPrime each time you come around in your loop.

You have a few other problems in your code. First, you need to increment p before using it again:

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
}

Second, you might be multithreaded, but you aren't concurrent. The code you have basically only allows one thread to run at a time:

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++;
}

Just my $0.02, but what you are trying to do might work, but the logic for selecting the next smallest prime will not always pick a prime, for example if one of the other threads hasn't processed that part of the array yet.

Here is an approach using an ExecutorService, there are some blanks (...) that you will have to fill in:

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

In Android, document still mention that we will get IllegalThreadStateException if the thread was already started .
However for some device it will not throw this exception (tested on Kyocera 7.0). In some popular device like Samsung, HTC, it throw throw the exception normally

I answer here because the Android question is mark as duplicated to this question.

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

Why does an IllegalThreadStateException occur when Thread.start is called again

Because JDK/JVM implementers coded Thread.start() method that way. Its a reasonable functional expectation to be able to restart a thread after a thread has completed its execution and that is what being suggested in chrisbunney's answer ( and I have put in a comment in that answer ) but if you look at Thread.start() implementation , the very first line is ,

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

where threadStatus == 0 means NEW state so my guess is that implementation doesn't resets this state to zero after execution has completed & thread is left in TERMINATED state ( non - zero state ). So when you create a new Thread instance on same Runnable , you basically reset this state to zero.

Also, I noticed the usage of word - may & never in same paragraph as different behavior is being pointed out by Phan Van Linh on some OSes,

It is never legal to start a thread more than once. In particular, a thread may not be restarted once it has completed execution.

I guess what they are trying to say in above Javadoc that even if you don't get IllegalThreadStateException on certain OS, its not legal in Java/Thread class way & you might get unexpected behavior.

The famous thread state diagrams depict the same scenario - no going back from dead state to new.

在此处输入图片说明

ThreadPools can be used for delivering tasks to set number of threads. When initiating you set the number of threads. Then you add tasks for the pool. And after you can block until all tasks have finished processing. Here is some sample code.

I am not at all sure I understand the question. All the methods for stopping threads that are executed from other threads are deprecated; the way to stop a thread is to have it check a variable that it and another thread can access (perhaps a volatile variable), and have the running thread check it occasionally to see if it should exit on its own.

I cannot tell why/whether you want to eliminate the running thread and use another one, and I cannot see how the different threads are going to help execute your overall goal any faster. But it's possible I'm just not understanding the math.

The Thread.isAlive() method can tell you if the Thread has already been started. Simply do this where you want to start your thread:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM