Java停止一个线程

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

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

public class MyRunnable implements Runnable {
	
	//定义一个变量作为是否允许运行的标志
    private volatile boolean isAllowedToRun = true;

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

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

	//停止线程的方法,原理是将"是否允许运行的变量"设为false, 是其线程不再循环
    public void stopThread() {
        isAllowedToRun = 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. 综合运用Thread的 interrupt() , isInterrupted(), interrupted`` , interrupted() , interrupted(), getAndClearInterrupt()

  • thread实例.interrupt()
  • thread实例.isInterrupted()
  • thread 的volatile变量 interrupted , 与静态方法interrupted()同名
  • Thread.interrupted() 就是调用 Thread.currentThread().getAndClearInterrupt() 完全等效

来自jdk21的源码集合, 调整了顺序,去掉原注释

	//用于标志是否中断的变量, 与静态方法 interrupted() 同名interrupt0()原生native底层方法
	volatile boolean interrupted;
	
	// 用于中断线程, 会将interrupted变量设为true, 并调用
    public void interrupt() {
        if (this != Thread.currentThread()) {
            checkAccess();

            // thread may be blocked in an I/O operation
            synchronized (interruptLock) {
                Interruptible b = nioBlocker;
                if (b != null) {
                    interrupted = true;
                    interrupt0();  // inform VM of interrupt
                    b.interrupt(this);
                    return;
                }
            }
        }
        interrupted = true;
        interrupt0();  // inform VM of interrupt
    }
    
	private native void interrupt0();
    
    
    public boolean isInterrupted() {
        return interrupted;
    }
    
    public static boolean interrupted() {
        return currentThread().getAndClearInterrupt();
    }

   
    boolean getAndClearInterrupt() {
        boolean oldValue = interrupted;
        if (oldValue) {
            interrupted = false;
            clearInterruptEvent();
        }
        return oldValue;
    }


    public final boolean isAlive() {
        return alive();
    }


    boolean alive() {
        return eetop != 0;
    }

可以用

  • interrupt()中断 配合 Thread.interrupted() 检查是否中断
  • interrupt()中断 配合 Thread.currentThread().isInterrupted() 检查是否中断

Thread.currentThread().isInterrupted()Thread.interrupted() 的不同在于:

Thread.currentThread().isInterrupted() 返回 interrupted属性的值, 其它什么也不做

	public boolean isInterrupted() {   return interrupted;    }

Thread.interrupted() 是调用 Thread.currentThread().getAndClearInterrupt(),

    public static boolean interrupted() {
        return currentThread().getAndClearInterrupt();
    }  

getAndClearInterrupt() 返回 interrupted , 如果 interrupted==trueinterrupted=falseinterrupted重设为false

    boolean getAndClearInterrupt() {
        boolean oldValue = interrupted;
        // We may have been interrupted the moment after we read the field,
        // so only clear the field if we saw that it was set and will return
        // true; otherwise we could lose an interrupt.
        if (oldValue) {
            interrupted = false;
            clearInterruptEvent();
        }
        return oldValue;
    }

调用 Thread.interrupted() 之后, volatile成员变量interrupted必为false

interrupt() 方法

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

public class MyRunnable implements Runnable {
    @Override
    public void run() {
    	// 循环判断条件可以用 !Thread.currentThread().isInterrupted() 或 Thread.interrupted()
        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 后执行清理操作并停止运行。



Thread.interrupted() 方法:

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

Thread t = new Thread(new Runnable() {
    public void run() {
    	// Thread.currentThread().getAndClearInterrupt() 可以等效换成 Thread.interrupted()
    	// Thread.interrupted() 源码就是调用 Thread.currentThread().getAndClearInterrupt() , 没有多余
        while (!Thread.interrupted()) {
            // 做一些工作
        }
    }
});
t.start();
// 在需要的时候停止线程
t.interrupt();

Thread.currentThread().getAndClearInterrupt() 可以等效换成 Thread.interrupted()
Thread.interrupted() 源码就是调用 Thread.currentThread().getAndClearInterrupt(), 没有多余

    public static boolean interrupted() {
        return currentThread().getAndClearInterrupt();
    }  

getAndClearInterrupt() 返回 interrupted , 如果 interrupted==trueinterrupted=falseinterrupted重设为false

    boolean getAndClearInterrupt() {
        boolean oldValue = interrupted;
        // We may have been interrupted the moment after we read the field,
        // so only clear the field if we saw that it was set and will return
        // true; otherwise we could lose an interrupt.
        if (oldValue) {
            interrupted = false;
            clearInterruptEvent();
        }
        return oldValue;
    }

isInterrupted() 返回 interrupted 其它什么也不做

	public boolean isInterrupted() {   return interrupted;    }




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







5. 不推荐使用 Thread.stop() 方法, 已无效,什么也不干,只抛个异常

stop()方法不但在1.2版就已经@Deprecated, 而且无效,什么也不干,只抛个异常
jdk21中的源码如下:

    @Deprecated(since="1.2", forRemoval=true)
    public final void stop() {
        throw new UnsupportedOperationException();
    }

在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线程。

10-28 02:47