停止来自其他 class 的线程

Stop thread from other class

我正在尝试停止实施 Runnable 的特定 Thread

我已经尝试了一些在谷歌搜索时发现的东西,但仍然找不到最佳答案。

我有两个Class

第一个是 Timer。 class 将创建一个倒计时。如果 countdown == 0 将结果发送给另一个 class。

public class Timer implements Runnable {

public Timer(int countdown, Room room, String cmdName) {
    this.room = room;
    this.countdown = countdown;
    this.cmdName = cmdName;
}

public void setResultThread(IResultThread resultThread) {
    this.resultThread = resultThread;
}

@Override
public void run() {

    for (int i = countdown; i >= 0; i--) {

        countdown--;
        if (i == 0) {
            resultThread.setResult(true);
        }

        try {
            TimeUnit.MILLISECONDS.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
}

第二个是User。我想停止此 class 中的 Thread

    Thread thread;
    String threadName = "player-" + user.getName();
    thread = utils.getThreadByName(threadName);

    if (thread != null) {
        thread.stop(); // i used stop before, but i read its not a good way
        thread.interrupt();
    }

    if (nextTurn == 4) {
        // do something
    } else {

        int countDown = 10
        //getting player name

        if (playerName != null) {
            String newThreadName = "player-" + playerName;

            Timer timer = new Timer(countDown, room, Send.PLAYER_TIMER.toString());
            thread = new Thread(timer, newThreadName);
            timer.setResultThread(resultThread);
            thread.start();
        }
    }

我不确定是我的代码有问题还是什么。 我尝试捕获异常,但 Thread 倒计时仍然是 运行。它似乎是在 Thread 处于睡眠状态时中断。我曾经尝试过使用 volatile 布尔值,但我认为在这种情况下这不是一个好的方法,当然它也不起作用。

我以前用过thread.stop(),但这不是个好主意。这是我第一次使用 Thread,我还是有点困惑。

我将不胜感激任何建议和回答。非常感谢。


回答

我给出了一个答案,以防万一有人和我有同样的问题。

@Override
public void run() {

    try {
        while (countdown >= 0) {

            TimeUnit.MILLISECONDS.sleep(1000);
            countdown--;
            if (countdown == 0) {
                resultThread.setResult(true);
            }
        }
    } catch (InterruptedException e) {
        countdown = 0;
        Thread.currentThread().interrupt();
        //e.printStackTrace();
    }
}

在我看来,停止计时器的首选方法是使用例如。取消方法

public class Timer implements Runnable {
  ...
  public void cancel() {
    countdown = 0;
  }
  ...
}

然后将 thread.stop() 替换为 timer.cancel(),显然您需要引用 timer 或让 Timer 扩展 Thread 而不是实施 Runnable.

正如 Andy 所说,在您的 run() 方法中它应该是

run() {
  ...
  try {
    for () {
      ...
    }
  } catch (...
}

您可以为每个线程指定一个名称,每当您想要关闭线程时,只需遍历集合以查找线程的确切名称并调用停止。

获取 运行 个线程的列表

获取可迭代集:

设置threadSet = Thread.getAllStackTraces().keySet();

不需要调用Thread.stop()

当您中断线程时您的代码当前没有停止的原因是中断的处理发生在循环内:

for (int i = countdown; i >= 0; i--) {
  // ...
  try {
    // Sleep
  } catch (InterruptedException e) {
  }
}

当线程在睡眠期间(或之前)被中断时,中断是"swallowed":你处理它,所以循环继续到下一次迭代,如果有的话。

有一个关于英国警察的小品罗宾·威廉姆斯,由于他们没有枪,不得不说 "Stop! Or.... I'll say stop again!"。如果你一直调用中断,最终你的线程会结束,因为最终循环会执行适当的次数。但是这样会很麻烦,也没有必要。

反转循环的顺序和 try/catch:

try {
  for (int i = countdown; i >= 0; i--) {
    // ...

    // Sleep
  }
} catch (InterruptedException e) {
}

现在,当中断发生时,执行移至循环之后,因此不再发生循环迭代。

(当然,您可以保留代码原样,只是 catch 中的 return;但是多个 return 点会使代码更难推理)


您还应该考虑在捕获 InterruptedException:

时重新中断当前线程
// ...
} catch (InterruptedException e) {
  Thread.currentThread().interrupt();
}

这会在当前线程上设置中断标志(令人困惑的是,这与 InterruptedException 正交),允许任何调用 Runnable 的代码知道线程已被中断。

严格来说没有必要这样做:如果您知道代码位于线程调用堆栈的顶部(就像这里:没有"caller"被告知中断)。但是重新中断线程几乎总是正确的做法 (*),并且在这种情况下实际上根本没有伤害。

(*) 如果您正在重新抛出 InterruptedException,或者如果您正在编写线程框架代码,并且您确信不这样做是正确的,那么您就不会这么做。

从另一个 class 停止线程的正确方法是中断 运行 线程。然后在线程内,您必须检测到中断,然后结束线程执行。

不要调用thread.stop,也不需要使用volatile变量

例如

public class StoppingThreadFromOtherClassExample {

    public static void main(String[] args) throws InterruptedException {
        StoppingThreadFromOtherClassExample stoppingThreadFromOtherClassExample = new StoppingThreadFromOtherClassExample();
        stoppingThreadFromOtherClassExample.doTheWork();
    }

    private void doTheWork() throws InterruptedException {

        Thread myThread = new Thread(new MyClassA());
        myThread.start();

        Thread.sleep(1000);
        myThread.interrupt();

        myThread.join();

    }
}

class MyClassA implements Runnable {

    int counter = 0;

    @Override
    public void run() {
        while (true) {
            counter++;
            System.out.println(counter);
            if (Thread.interrupted()) {
                System.out.println("this thread was interrupted. it will stop now!!");
                break;
            }
        }
    }
}