Как остановить поток без использования метода остановки?

1

В следующей программе Java я сохраняю 3 потока в HashMap, я создал 3 объекта класса Th, которые расширены из класса Thread (я попытался выполнить Runnable, но он также не работает!), Я хочу остановить t2 после 3sec путем назначения нулевого значения, я не могу остановить его, я не хочу использовать метод stop(), поскольку он устарел, и он работает только, если я запускаю потоки с использованием метода start, пока он не работает, если я запускаю потоки используя метод выполнения 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);
        }
    }
}
  • 2
    Вам нужно будет реализовать механизм прерывания потока. Установка значения в HashMap на null абсолютно ничего не делает с потоком.
  • 1
    (Кроме того, для вашего класса нет смысла расширять Thread , если вы отправляете его экземпляры в ExecutorService как Runnable s.)
Показать ещё 6 комментариев
Теги:
multithreading

7 ответов

1

Это всего лишь пример одного потока. Создайте статический volatile булевский флаг.

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

когда вы хотите остановить поток, просто установите RUN_THREAD_FLAG = false

PS: Вы должны всегда заканчивать нить должным образом, насколько это возможно, используя некоторые FLAG или другие методы. Не используйте какой-либо метод прерывания, насколько это возможно.

  • 1
    сделайте этот флаг нестабильным, пожалуйста. Без этого не гарантируется, что другой поток когда-нибудь его заберет (например, если значение находится в кэше процессора)
  • 0
    да, спасибо за Heads-UP! :)
Показать ещё 2 комментария
0

Когда вы создаете экземпляры Th расширяющегося потока и передаете их в ExecutorService, исполнитель не запускает задачу в переданном потоке. Он просто знает, что вы дали ему Runnable, и он продолжает запускать его на одном из своих рабочих потоков.

Вот почему вы не видите флаг прерывания, который устанавливается при использовании службы executorservice. Это также почему это работает, когда вы начинаете свои собственные темы. Вы ищете прерывание с

this.isInterrupted()

где this поток, в который вы проходили, но это поток рабочего пула, а не экземпляр Th, который вызывает прерывание. Измените свой чек на

Thread.currentThread().isInterrupted()

Вы также должны установить флаг прерывания, когда вы поймаете InterruptedException. Когда метод sleep выбрасывает InterruptedException, он очищает флаг. Как и выше, вы должны вызывать прерывание по текущему потоку, а не по this вопросу.

Не расширяйте Thread больше, вы просто создаете возможности для путаницы.

0
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");
}
}
  • 0
    Добавление объяснения того, как этот код отвечает на вопрос, поможет будущим посетителям.
  • 0
    спасибо за комментарий :). вышеупомянутая программа была написана для остановки потока, когда пользователь нажимает кнопку ввода.
0

Во-первых, НЕ расширяйте Thread для выполнения задач (= часть работы)! Я думаю, вы неправильно поняли структуру исполнителя. Он уже заботится о Threads, вам не нужно создавать его самостоятельно! То, что вы хотите сделать, - это реализовать задачи (= внедрить Runnable), которые выполняются Executor.

Во-вторых, ваши задачи должны быть чувствительны к прерыванию, то есть прерывание исполняемого потока путем вызова interrupt() должно привести к тому, что ваша задача завершит свою работу:

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

Обратите внимание, что мы можем использовать бесконечный цикл здесь Thread.sleep выбросит InterruptedException, который будет выйти из цикла, как только выполнение Thread прерывается. (В противном случае нам нужно было бы проверить флаг прерывания, вызвав Thread.currentThread().isInterrupted самим собой).

В-третьих, используйте ScheduledExecutorService для обоих, выполнения ваших задач и отмены:

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

Последняя строка кода использует лямбда-выражения, доступные с Java 8. Если вы не хотите использовать их или не можете использовать их по какой-либо причине, снова используйте обычный Runnable:

executor.schedule(new Runnable() {
    public void run() { t2f.cancel(true); }
}, 3, TimeUnit.SECOND);
0

Вы также можете попробовать аналогичное решение, как показано ниже:

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

открытый класс 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();
}

} класс 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();
            }

        }
    }
}

}

0

Ваше изменение на использование t2.interrupt будет работать, но вам нужно изменить потоки, чтобы они выглядели следующим образом: (Обратите внимание на оператор return).

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

Когда метод сна прерывается, он выдает InterruptedException, но очищает флаг до того, как он это сделает. Вы должны сделать что-то внутри исключения, чтобы заставить его работать. Другой способ:

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

Обратите внимание: поскольку запуск выполняется только из одного потока, теперь он не должен быть изменчивым.

  • 0
    Я думаю, что вообще плохая идея останавливать весь поток только потому, что сон был прерван. IMHO Это зависит от логики в потоке, если это правильный случай. В случае запрашивающей стороны решение от @kaze должно быть предпочтительным.
  • 0
    @kaze Мне было интересно об этом - вы поддерживаете это заявление? В основном мне было интересно, всегда ли работает прерывание. Я полагаю, что проблема была бы, если бы вы делали что-то другое (например, слушали сокет), ИТ-специалисты могли бы выбросить прерванное исключение и снять флаг, и вы бы не получили его в своем заявлении о сне - но я не смог придумать действительно хорошая причина не снимать флажок в улове.
0

Проверяя свой код, нельзя даже остановить t2 используя stop потому что t2 никогда не запускается, он выполняется executor. Чтобы остановить t2 вам нужно запустить t2 напрямую: (изменения, внесенные в ваш последний отредактированный код: установите флаг прерывания = true, если поток был прерван во sleep и выполнил t2.start() вместо 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);
        }
    }
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню