如何从另一个方法 stop/interrupt 运行 线程?

How to stop/interrupt running thread from another method?

我完全是 android 和 Java 开发的初学者,我目前正在尝试制作节拍器。 在让声音播放正常工作后,我遇到的第一个问题是,每当节拍器播放时,应用程序就会停止响应 - 那是我了解线程以及如何使用新线程进行音频播放的时候。

创建新线程有帮助,现在应用程序运行正常,但我无法将线程转到 stop/interrupt。我已经阅读了大约 50 篇关于线程和中断的文章,但我无法理解。

这是我的 'Player' class 代码,大部分是我从另一个 Stack Overflow post 复制的(我尝试了无数其他方法和变体 none工作):

package com.example.t.firstapp;

import android.util.Log;

public class Player implements Runnable {

    Thread backgroundThread;
    Metronome m;


    public void start() {
        if (backgroundThread == null) {
            backgroundThread = new Thread(this);
            m = new Metronome();
            backgroundThread.start();
        }
    }

    public void stop() {
        if (backgroundThread != null) {
            backgroundThread.interrupt();
        }
    }

    public void run() {
        try {
            Log.i("a", "Thread starting.");
            while (!backgroundThread.isInterrupted()) {
                m.play();
            }
            Log.i("b", "Thread stopping.");
            throw new InterruptedException(); // ???
        } catch (InterruptedException ex) {
            // important you respond to the InterruptedException and stop processing
            // when its thrown!  Notice this is outside the while loop.
            Log.i("c", "Thread shutting down as it was requested to stop.");
        } finally {
            backgroundThread = null;
        }

    }
}

注意标有“???”的行。我自己添加了那个,否则 "catch (InterruptedException ex)" 会返回一个错误。 这是我的 MainActivity class:

中的相关代码
public class MainActivity extends AppCompatActivity {

...

public Player p;

...
    public void play() {
        p = new Player();
        p.start();
    }
    public void stop() {
        p.stop();
    }
}

调用p.stop();从方法 'stop' 中实际上并没有做任何事情。这就是我卡住的地方。如果我在启动线程后立即调用 p.stop(),如下所示:

public void play() {
    p = new Player();
    p.start();
    p.stop();
}

然后它开始工作,我看到了来自播放器的所有相关日志消息 class。当我从 'stop' 方法中调用它时,为什么 p.stop() 不起作用?是因为我从不同的方法调用它,还是因为我没有立即调用它?

任何帮助将不胜感激,因为这非常令人沮丧。我一直在研究和实践 Android 开发现在只有一个星期,但在过去的 5 天里我什么也没做,但试图解决这个问题。谢谢

你误解了中断的概念。中断并不是强制线程停止的神奇方法,而是它只适用于 支持中断的方法 - 比如睡眠。

看看 Thread#interrupt() API,其中列出了中断支持的方法:

If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.

If this thread is blocked in an I/O operation upon an interruptible channel then the channel will be closed, the thread's interrupt status will be set, and the thread will receive a ClosedByInterruptException.

If this thread is blocked in a Selector then the thread's interrupt status will be set and it will return immediately from the selection operation, possibly with a non-zero value, just as if the selector's wakeup method were invoked.

If none of the previous conditions hold then this thread's interrupt status will be set.

通过不断检查中断状态,您可以通过中断支持很好地实现自己的方法。


现在让我们看看如何解决您的问题。

根据您的评论,m.play() 不会 return,也就是说,一旦 m.play() 被调用,while 就不会检查线程是否已被中断;反过来它永远不会停止,因为 m.play() 没有实现支持中断 。这也可以解释为什么编译器抱怨没有人抛出 InterruptedException。 (如果立即中断它会起作用的原因是中断状态在它到达while之前改变了......想想看。)

现在,我假设,如果您将调用 m.stop()m.play() return,成功地重新检查线程中断。正如评论中所述,这就是它起作用的原因。

但是看,中断线程没有实际用处 - 因为您所要做的就是调用 m.stop() 并释放 m.play(),只需播放并等待 return -这意味着停止已被调用。与while循环相同,一路放下。

public void run() {
    Log.i("a", "Thread starting.");

    m.play(); // blocks till stopped from some other thread...
    
    Log.i("b", "Thread stopping.");
    Log.i("c", "Thread shutting down as it was requested to stop.");
   
    backgroundThread = null;
}

我可能会看到使用 while 和中断的情况,如果 m.play() 可能 return 早于调用 m.stop()(例如,通过某些异常),并且您想重新启动节拍器,直到调用 stop;那么一个循环可能正在救援,并且中断可能会通过调用 m.stop().

发出信号表明它实际上已停止
public void run() {
    Log.i("a", "Thread starting.");
    while (!backgroundThread.isInterrupted()) {
        m.play();
        if(!backgroundThread.isInterrupted())
            Log.i("b", "Stopped by exception, restarting....");
    }

    Log.i("c", "Thread stopping.");
    Log.i("d", "Thread shutting down as it was requested to stop.");

    backgroundThread = null;
}