简体   繁体   中英

How to stop a thread without using the stop Method?

In the Following Java Program I am storing 3 threads in a HashMap, I have created 3 objects of Th class which is extended from Class Thread (I have tried by Implementing the Runnable but it is also not working!), I want to stop the t2 after 3sec by assigning null value I am not able to stop it , I dont want to use the stop() Method as it is deprecated and it only works if I start the threads using start method while it does not work if I start the threads using the execute method of ExecutorService

public class TestingThreads {

    static HashMap<String, Thread> trds = new HashMap<>();

    static Th t1 = new Th("Th1");
    static Th t2 = new Th("Th2");
    static Th t3 = new Th("Th3");

    public TestingThreads() {

    }

    public static void main(String[] args) {
        long ct = System.currentTimeMillis();

        ExecutorService executor = Executors.newCachedThreadPool();

        trds.put("t1", t1);
        trds.put("t2", t2);
        trds.put("t3", t3);
        executor.execute(t1);
        executor.execute(t2);
        executor.execute(t3);
        executor.shutdown();
        new Thread() {

            @Override
            public void run() {
                while (true) {
                    if (ct + 3000 < System.currentTimeMillis()) {
                        trds.put("t2", null);
                        trds.remove("t2");
                        System.out.println("Size = " + trds.size());
  //I dont wanna use the stop() method as is it deprecated and it only works when I use start method to run the Thread while it is not working with execute() method of class ExecutorService 
                        // t2.stop();
                        t2 = null;
                        break;
                    }
                }
            }

        }.start();
    }
}

class Th extends Thread {

    String name;
    Random rm = new Random();

    public Th(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        while (!this.isInterrupted()) {
            int i = rm.nextInt(500);
            try {
                Thread.sleep(i);
            } catch (InterruptedException ex) {
            }
            System.out.println(name + " " + i);
        }
    }
}

This is just an example of one thread.. Create a static volatile boolean flag.

static volatile boolean RUN_THREAD_FLAG= true;
Thread yourThread= new Thread(){
    @Override
    public void run() {
        try{
            // you can add your other conditions inside while condition
            // and AND it with the FLAG
            //while(RUN_THREAD_FLAG && yourCondition)
            while(RUN_THREAD_FLAG){ 
                sleep(SLEEP_TIME); //OR your task
            }
        } catch(Exception e){
                e.printStackTrace();
            }
    }
};
yourThread.start();

when ever you want to stop the thread, just set RUN_THREAD_FLAG= false

PS: You should always end the thread properly as long as possible using some FLAG or other methods. Don't use any interrupt method as far as you can.

Inspecting your code, can't even stop t2 using stop because t2 is never started, it is executed by executor . To stop t2 you need to run t2 directly: (changes done to your last edited code: set the interrupt flag = true if case the thread was interrupted in sleep , and executing t2.start() instead of executor.execute(t2) )

public class TestingThreads {

    static HashMap<String, Thread> trds = new HashMap<>();

    static Th t1 = new Th("Th1");
    static Th t2 = new Th("Th2");
    static Th t3 = new Th("Th3");

    public TestingThreads() {

    }

    public static void main(String[] args) {
        long ct = System.currentTimeMillis();

        ExecutorService executor = Executors.newCachedThreadPool();

        trds.put("t1", t2);
        trds.put("t2", t2);
        trds.put("t3", t3);
        executor.execute(t1);
        /*executor.execute(t2);*/ t2.start();
        executor.execute(t3);
        executor.shutdown();
        new Thread() {

            @Override
            public void run() {
                while (true) {
                    if (ct + 3000 < System.currentTimeMillis()) {
                        trds.put("t2", null);
                        trds.remove("t2");
                        System.out.println("Size = " + trds.size());
                        // t2.stop();
                        t2.interrupt();
                        t2 = null;
                        break;
                    }
                }
            }

        }.start();
    }
}

class Th extends Thread {

    String name;
    Random rm = new Random();

    public Th(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        while (!this.isInterrupted()) {
            int i = rm.nextInt(500);
            try {
                Thread.sleep(i);
            } catch (InterruptedException ex) {
                this.interrupt(); // this line was also added
                // or better use "return" as suggested by Bill K
            }
            System.out.println(name + " " + i);
        }
    }
}

You can also try similar solution as below:-

import java.util.HashMap; import java.util.Random;

public class TestingThreads {

static Th t1 = new Th("Th1");
static Th t2 = new Th("Th2");
static Th t3 = new Th("Th3");

public TestingThreads() {

}
public static void main(String[] args) {
    t1.start();
    t2.start();
    t3.start();
}

} class Th extends Thread {

String name;
Random rm = new Random();

public Th(String name) {
    this.name = name;
}
@Override
public void run() {

    final long ct = System.currentTimeMillis()+3000;
    Thread t= Thread.currentThread();
    System.out.println("Running Thread is :-"+t.getName()+"  "+System.currentTimeMillis());
    while (true) {
        if (ct < System.currentTimeMillis()) {
            try{
                System.out.println("Running Thread is :-"+t.getName()+"  "+System.currentTimeMillis());
                t.interrupt();
                break;
            }
            catch(Exception ie)
            {
                System.out.println(t.getName()+" closed");
                ie.printStackTrace();
            }

        }
    }
}

}

Your change to use t2.interrupt will work, but you need to change the threads to look like this: (Note the return statement).

@Override
public void run() {
    while (!this.isInterrupted()) {
        int i = rm.nextInt(500);
        try {
            Thread.sleep(i);
        } catch (InterruptedException ex) {
            return;
        }
        System.out.println(name + " " + i);
    }
}

When the sleep method is interrupted it throws the InterruptedException but clears the flag before it does so. You have to do something inside the exception to make it work. Another way is:

boolean running=true;
@Override
public void run() {
    while (running) {
        int i = rm.nextInt(500);
        try {
            Thread.sleep(i);
        } catch (InterruptedException ex) {
            running=false;
        }
        System.out.println(name + " " + i);
    }
}

Note that since running is only accessed from one thread now it doesn't have to be volatile.

First , do NOT extend Thread for implementing tasks (= piece of work)! I think you've misunderstood the executor framework. It already takes care of the Threads , you do not have to create one by yourself! What you want to do is to implement tasks (= implement Runnable ) that get executed by an Executor .

Second , your tasks should be sensitive to interruption, ie interrupting the executing thread by calling interrupt() should cause your task to finish its work:

public class Task implements Runnable {
    public void run() {
        try {
            while (true) {
                int i = rm.nextInt(500);
                Thread.sleep(i);
                System.out.println(name + " " + i);
            }
        } catch (InterruptedException ex) {
            // restore interruption flag
            Thread.currentThread.interrupt();
        }
    }
}

Note that we can use an infinite loop here as Thread.sleep will throw an InterruptedException that will exit the loop as soon as the executing Thread gets interrupted. (Otherwise we'd have to check the interruption flag by calling Thread.currentThread().isInterrupted by ourselves).

Third , use a ScheduledExecutorService for both, execution of your tasks and cancelling:

ScheduledExecutorService exectuor = Executors.newScheduledThreadPool(4);
Future<?> t1f = executor.execute(new Task());
Future<?> t2f = executor.execute(new Task());
Future<?> t3f = executor.execute(new Task());

// now we can cancel tasks individually after a certain amount of time:
executor.schedule(() -> t2f.cancel(true), 3, TimeUnit.SECOND);

The last line of code uses lambda expressions available since Java 8. If you don't want to use them or cannot use them for some reason, use a normal Runnable again:

executor.schedule(new Runnable() {
    public void run() { t2f.cancel(true); }
}, 3, TimeUnit.SECOND);
using a boolean variable:

package com.Exception.Demos;

public class MyRunnable implements Runnable {

    boolean continueThread=true;
    @Override
    public void run() {
        // TODO Auto-generated method stub

        int i=0;
        while(true){
            if(continueThread){
                try{
                    System.out.println(i++);
                    Thread.sleep(1000);
                    System.out.println("press enter to stop "+Thread.currentThread().getName());
                }catch(InterruptedException exception){
                    exception.printStackTrace();
                }
            }
            else{
                System.out.println(Thread.currentThread().getName()+"Ended");
                break;
            }
        }
    }

}
package com.Exception.Demos;

import java.io.IOException;

public class TerminatingAThread {
public static void main(String[] args) throws IOException {
    MyRunnable myRunnable=new MyRunnable();
    Thread thread=new Thread(myRunnable,"Thread1");
    thread.start();
    System.out.println(Thread.currentThread().getName()+"thread waiting for user to press enter");
    System.in.read();
    myRunnable.continueThread=false;
    System.out.println(Thread.currentThread().getName()+"thread ended");
}
}

Two main lessons here:

  • Don't extend Thread, it just creates opportunities for confusion. (Even if you don't use executors, separating the logic to be executed from the means of executing the task improves clarity.)

  • Thread interruption is how tasks get canceled and how executors get shut down in java.util.concurrent, you must understand it in order to use executors effectively.

Now for details:

Thread implements Runnable. When you create instances of Th extending Thread and pass them into the ExecutorService, the executor doesn't run the task on the passed-in thread. It just knows you gave it a Runnable, and it proceeds to execute that Runnable on one of its worker threads.

That is why you're not seeing the interrupt flag get set when you use the ExecutorService. It also explains why it works when you start your own threads. You're looking for the interruption with

this.isInterrupted()

where this is the thread you passed in, but it's the pool's worker thread, not the Th instance, that gets interrupt called on it. Change your check to

Thread.currentThread().isInterrupted()

You should also set the interrupt flag when you catch an InterruptedException. When the sleep method throws InterruptedException it clears the flag (so in the next while loop iteration isInterrupted returns false). Like above, you should call interrupt on the current thread, not on this .

The thread waiting for the executor to finish is unneeded (and the busy waiting it's doing is not good), you can call awaitTermination instead.

Also using shutdown doesn't work because these tasks never quit on their own, they have to be interrupted. shutdown politely waits for current tasks to finish, which will never happen; you need shutdownNow in order to interrupt the tasks.

Here is a simpler program to do the same thing, it submits 3 tasks and lets them execute for 3 seconds before shutting down. Notice all the things I don't have to do, I don't have to squirrel the tasks away in a hashmap and I don't need a watchdog thread to wait for the executor to terminate.

import java.util.*;
import java.util.concurrent.*;

public class TestThreads {

    public static void main(String... args) throws Exception {
        ExecutorService executor = Executors.newCachedThreadPool();
        executor.execute(new Th("t1"));
        executor.execute(new Th("t2"));
        executor.execute(new Th("t3"));
        Thread.sleep(3000L);
        executor.shutdownNow();
        executor.awaitTermination(10000L, TimeUnit.MILLISECONDS);
        System.out.println("executor terminated=" + executor.isTerminated());
    }
}

class Th implements Runnable {

    String name;
    Random rm = new Random();

    public Th(String name) {
        this.name = name;
    }

    @Override public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            int i = rm.nextInt(500);
            try {
                Thread.sleep(i); 
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println(name + " " + i);
        }
    }
}

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