Java停止一个线程 笔记241026

80 阅读8分钟

Java停止一个线程

1. 使用共享变量(标志位)

通过设置一个共享变量(标志位)来通知线程停止运行。

public class MyRunnable implements Runnable {
    private volatile boolean running = true;

    @Override
    public void run() {
        while (running) {
            // 执行一些任务
            System.out.println("Thread is running...");

            // 模拟一些工作
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // 恢复中断状态
            }
        }
        System.out.println("Thread is stopping...");
    }

    public void stopThread() {
        running = false;
    }

    public static void main(String[] args) throws InterruptedException {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();

        // 让主线程等待一段时间,然后停止子线程
        Thread.sleep(5000);
        myRunnable.stopThread();
    }
}




2. 使用 interrupt() 方法

通过调用线程的 interrupt() 方法来设置线程的中断状态,并在线程内部检查这个状态来决定是否退出。

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            // 执行一些任务
            System.out.println("Thread is running...");

            // 模拟一些工作
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // 当线程被中断时,会抛出 InterruptedException,并清除中断状态
                // 通常,我们会在这里重新设置中断状态,或者做一些清理工作
                Thread.currentThread().interrupt(); // 重新设置中断状态,以便上层调用者知道中断发生了
            }
        }
        System.out.println("Thread is stopping...");
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new MyRunnable());
        thread.start();

        // 让主线程等待一段时间,然后中断子线程
        Thread.sleep(5000);
        thread.interrupt();
    }
}

在Java中,interrupt() 方法是用于中断线程的一种机制。当你调用一个线程的 interrupt() 方法时,该线程的中断状态将被设置为 true。线程本身并不会立即停止执行,而是会在其运行过程中检查这个中断状态,并根据需要作出响应。

中断机制的工作原理
  1. 设置中断状态:当你调用 interrupt() 方法时,目标线程的中断状态被设置为 true。这个状态是一个布尔值,表示线程是否已经被中断。

  2. 检查中断状态:线程在其执行过程中应该定期检查这个中断状态。这通常是通过检查 Thread.interrupted()(静态方法,会清除中断状态)或 Thread.currentThread().isInterrupted()(实例方法,不会清除中断状态)来实现的。

  3. 响应中断:一旦线程检测到它已被中断(即中断状态为 true),它应该根据设计的逻辑来响应这个中断。这可能意味着立即退出循环、执行一些清理操作、抛出 InterruptedException(如果线程正在执行一个可中断的阻塞操作,如 Thread.sleep()Object.wait()Selector.select()),或者执行其他任何适当的操作。

注意事项
  • 中断是一种协作机制:线程不会立即停止执行,而是会在适当的时候检查并响应中断。这意味着线程必须自己编写代码来检查中断状态并作出反应。

  • 清除中断状态:调用 Thread.interrupted() 方法会返回当前线程的中断状态,并将该状态设置为 false。如果你希望保留中断状态以便稍后检查,应该使用 Thread.currentThread().isInterrupted() 方法。

  • 可中断的阻塞方法:一些Java库中的方法(如 Thread.sleep()Object.wait()Selector.select())是可中断的。当这些方法被调用时,如果线程被中断,它们会抛出 InterruptedException 并清除中断状态。这意味着,如果你的线程在这些方法上阻塞,并且你希望它能够在被中断时立即响应,那么你应该捕获这个异常并适当地处理它。

  • 不可中断的阻塞:有些操作(如I/O操作或某些类型的同步)可能不是可中断的。在这种情况下,你可能需要实现自己的机制来检查中断状态并响应中断,例如通过轮询中断状态或使用其他形式的线程间通信。

示例代码

以下是一个使用 interrupt() 方法来中断线程的示例:

public class InterruptableThread extends Thread {
    @Override
    public void run() {
        try {
            while (!Thread.currentThread().isInterrupted()) {
                // 执行一些任务
                System.out.println("Thread is running...");

                // 模拟一些工作,这里使用sleep来模拟阻塞
                Thread.sleep(1000);

                // 可以在这里添加其他逻辑来检查中断状态并响应中断
            }
        } catch (InterruptedException e) {
            // 如果线程在sleep时被中断,InterruptedException会被抛出
            // 在这里处理中断,例如执行清理操作
            System.out.println("Thread was interrupted during sleep. Cleaning up...");

            // 通常,在捕获InterruptedException后,应该再次设置中断状态,
            // 因为InterruptedException并不总是表示线程应该完全停止。
            // 这取决于你的应用程序的逻辑。
            // 在这个简单的例子中,我们不需要再次设置中断状态。
        }
        System.out.println("Thread is stopping...");
    }

    public static void main(String[] args) throws InterruptedException {
        InterruptableThread thread = new InterruptableThread();
        thread.start();

        // 让主线程等待一段时间,然后中断子线程
        Thread.sleep(5000);
        thread.interrupt();

        // 等待子线程完全停止(在实际应用中,你可能不需要这样做,
        // 因为这可能会导致主线程阻塞。这里只是为了演示目的。)
        thread.join();
    }
}

在这个示例中,线程会每秒打印一次消息,直到它被中断。主线程在启动子线程后等待5秒,然后调用子线程的 interrupt() 方法来中断它。子线程在捕获到 InterruptedException 后执行清理操作并停止运行。




3. 使用 FutureExecutorService

对于需要更高级控制的情况,可以使用 ExecutorServiceFuture 来管理线程,并通过 Future.cancel() 方法来停止线程。不过,这通常适用于那些可以通过 Callable 任务提交给 ExecutorService 的情况。

import java.util.concurrent.*;

public class MyCallable implements Callable<Void> {
    @Override
    public Void call() throws Exception {
        while (!Thread.currentThread().isInterrupted()) {
            // 执行一些任务
            System.out.println("Thread is running...");

            // 模拟一些工作
            Thread.sleep(1000);
        }
        System.out.println("Thread is stopping...");
        return null;
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Future<Void> future = executorService.submit(new MyCallable());

        // 让主线程等待一段时间,然后取消任务
        Thread.sleep(5000);
        future.cancel(true); // 尝试停止任务,如果正在运行则中断线程

        // 关闭ExecutorService
        executorService.shutdown();
    }
}




4. 使用 Thread.interrupted() 方法:

这个方法会检查当前线程是否被中断,并且会清除当前线程的中断状态。

Thread t = new Thread(new Runnable() {
    public void run() {
        while (!Thread.interrupted()) {
            // 做一些工作
        }
    }
});
t.start();
// 在需要的时候停止线程
t.interrupt();




5. 不推荐使用 Thread.stop() 方法:

在Java中,Thread.stop() 方法是一个已废弃(deprecated)的方法,用于强制终止线程的执行。然而,由于其潜在的危险性和不可预测性,这种方法在现代Java编程中通常是不被推荐的。

为什么 Thread.stop() 被废弃?
  1. 数据不一致:强制终止线程可能会导致它正在操作的数据处于不一致的状态。例如,如果线程在更新多个相关数据结构时被停止,那么这些数据可能会部分更新,从而导致数据损坏。

  2. 死锁风险Thread.stop() 方法会立即释放线程所持有的所有监视器锁。这可能会导致其他线程试图获取这些锁时发生死锁,因为释放锁的操作并不是在安全的上下文中进行的。

  3. 无法清理资源:被强制终止的线程可能没有机会执行任何清理代码,如关闭文件、释放数据库连接等。

  4. 不安全的操作Thread.stop() 方法可能会导致不安全的操作,因为它会立即停止线程,而不考虑线程当前正在执行的操作的性质。

替代方案

由于 Thread.stop() 的上述缺陷,Java推荐使用更安全和更可控的方式来停止线程:

  1. 使用共享变量(标志位):线程可以定期检查一个共享变量(通常是一个volatile修饰的布尔值),以确定是否应该停止执行。

  2. 使用 interrupt() 方法:通过调用线程的 interrupt() 方法来设置线程的中断状态。线程内部应该定期检查这个状态,并在适当的时候响应中断,通常是通过抛出 InterruptedException 或执行一些清理操作。

  3. 使用 ExecutorServiceFuture:对于需要更高级线程管理的场景,可以使用 ExecutorService 来提交和管理任务。通过调用 Future.cancel() 方法,可以请求取消任务的执行。如果任务正在运行,那么它会收到一个中断请求。

示例代码

以下是一个使用 interrupt() 方法来停止线程的示例:

public class SafeStopThread extends Thread {
    @Override
    public void run() {
        try {
            while (!Thread.currentThread().isInterrupted()) {
                // 执行一些任务
                System.out.println("Thread is running...");

                // 模拟一些工作
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            // 线程被中断时,InterruptedException 被抛出
            // 可以在这里执行一些清理操作
            System.out.println("Thread was interrupted. Cleaning up...");
        }
        System.out.println("Thread is stopping...");
    }

    public static void main(String[] args) throws InterruptedException {
        SafeStopThread thread = new SafeStopThread();
        thread.start();

        // 让主线程等待一段时间,然后中断子线程
        Thread.sleep(5000);
        thread.interrupt();
    }
}

在这个示例中,线程会定期检查它的中断状态,并在被中断时执行清理操作,然后安全地停止。这种方法比使用 Thread.stop() 更加安全和可控。

注意事项

  1. 避免使用 Thread.stop():这是不推荐的方法,因为它会立即停止线程,并且不会释放任何监视器锁,可能会导致数据不一致和死锁。
  2. 优雅地处理中断:当线程被中断时,应该优雅地处理中断状态,确保资源的正确释放和任务的正确终止。

通过以上方法,你可以更安全和优雅地停止一个Java线程。