В следующей программе 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);
}
}
}
Это всего лишь пример одного потока. Создайте статический 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 или другие методы. Не используйте какой-либо метод прерывания, насколько это возможно.
Когда вы создаете экземпляры Th расширяющегося потока и передаете их в ExecutorService, исполнитель не запускает задачу в переданном потоке. Он просто знает, что вы дали ему Runnable, и он продолжает запускать его на одном из своих рабочих потоков.
Вот почему вы не видите флаг прерывания, который устанавливается при использовании службы executorservice. Это также почему это работает, когда вы начинаете свои собственные темы. Вы ищете прерывание с
this.isInterrupted()
где this
поток, в который вы проходили, но это поток рабочего пула, а не экземпляр Th, который вызывает прерывание. Измените свой чек на
Thread.currentThread().isInterrupted()
Вы также должны установить флаг прерывания, когда вы поймаете InterruptedException. Когда метод sleep выбрасывает InterruptedException, он очищает флаг. Как и выше, вы должны вызывать прерывание по текущему потоку, а не по this
вопросу.
Не расширяйте Thread больше, вы просто создаете возможности для путаницы.
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");
}
}
Во-первых, НЕ расширяйте 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);
Вы также можете попробовать аналогичное решение, как показано ниже:
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();
}
}
}
}
}
Ваше изменение на использование 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);
}
}
Обратите внимание: поскольку запуск выполняется только из одного потока, теперь он не должен быть изменчивым.
Проверяя свой код, нельзя даже остановить 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);
}
}
}
HashMap
наnull
абсолютно ничего не делает с потоком.Thread
, если вы отправляете его экземпляры вExecutorService
какRunnable
s.)