java中使用interrupt通知线程停止

本文详细介绍了Java中正确停止线程的方法,包括普通情况、可能被阻塞的情况以及每次迭代后都阻塞的情况下的线程停止策略。通过具体示例展示了如何使用Thread.interrupt()方法并结合线程状态检测来优雅地停止线程。

普通情况停止线程

public class RightWayStopThreadWithoutSleep implements Runnable {

    @Override
    public void run() {
        int num = 0;
        while (!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) {
            if (num % 10000 == 0) {
                System.out.println(num + "是1W的倍数");
            }
            num++;
        }
        System.out.println("任务运行结束!");
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
        thread.start();
        // 等待1s
        Thread.sleep(1000);
        // 通知停止线程
        thread.interrupt();
    }
}
复制代码

使用 thread.interrupt() 通知线程停止

但是 线程需要配合

while 中使用 Thread.currentThread().isInterrupted() 检测线程当前的状态

运行结果:

……
……
221730000是1W的倍数
221740000是1W的倍数
221750000是1W的倍数
221760000是1W的倍数
221770000是1W的倍数
221780000是1W的倍数
221790000是1W的倍数
221800000是1W的倍数
任务运行结束!

Process finished with exit code 0
复制代码

在可能被阻塞情况下停止线程

public class RightWayStopThreadWithSleep {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            int num = 0;
            while (num <= 300 && !Thread.currentThread().isInterrupted()) {
                if (num % 100 == 0) {
                    System.out.println(num + "是100的倍数");
                }
                num++;
            }
            try {
                // 等个1秒,模拟阻塞
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("线程已停止!!");
                e.printStackTrace();
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();
        // 等待时间要小于上面设置的1秒,不然线程都运行结束了,才执行到下面的thread.interrupt();代码
        Thread.sleep(500);
        // 通知停止线程
        thread.interrupt();
    }
}
复制代码

线程在sleep 1秒的过程中,收到interrupt信号被打断,

线程正在sleep过程中响应中断的方式就是抛出 InterruptedException 异常

运行结果:

0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
线程已停止!!
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at stopthreads.RightWayStopThreadWithSleep.lambda$main$0(RightWayStopThreadWithSleep.java:19)
    at java.lang.Thread.run(Thread.java:748)

Process finished with exit code 0
复制代码

在每次迭代后都阻塞的情况下停止线程

public class RightWayStopThreadWithSleepEveryLoop {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            int num = 0;
            try {
                while (num <= 10000) {
                    if (num % 100 == 0) {
                        System.out.println(num + "是100的倍数");
                    }
                    num++;
                    // 每次循环都要等待10毫秒,模拟阻塞
                    Thread.sleep(10);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();
        // 5秒后通知停止线程
        Thread.sleep(5000);
        thread.interrupt();
    }
}
复制代码

当每次迭代都会让线程阻塞一段时间的时候,在 while/for 循环条件判断时,

是不需要使用 *Thread.currentThread().isInterrupted() *判断线程是否中断的

运行结果:

0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
400是100的倍数
java.lang.InterruptedException: sleep interrupted
复制代码

如果将上述代码中的 try/catch 放在 while 循环内

public class RightWayStopThreadWithSleepEveryLoop {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            int num = 0;
            while (num <= 10000) {
                if (num % 100 == 0) {
                    System.out.println(num + "是100的倍数");
                }
                num++;
                try {
                    // 每次循环都要等待10毫秒,模拟阻塞
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();
        // 5秒后通知停止线程
        Thread.sleep(5000);
        thread.interrupt();
    }
}
复制代码

运行结果:

0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
400是100的倍数
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at stopthreads.RightWayStopThreadWithSleepEveryLoop.lambda$main$0(RightWayStopThreadWithSleepEveryLoop.java:18)
    at java.lang.Thread.run(Thread.java:748)
500是100的倍数
600是100的倍数
700是100的倍数
……
……

复制代码

会发现虽然抛出了异常,但是程序并没有停止,还在继续输出,

即使在 while 条件判断处添加 !Thread.currentThread().isInterrupted() 条件,依然不能停止程序!

原因是

java语言在设计 sleep() 函数时,有这样一个理念:

就是当它一旦响应中断,便会把 interrupt 标记位清除。

也就是说,虽然线程在 sleep 过程中收到了 interrupt 中断通知,并且也捕获到了异常、打印了异常信息,

但是由于 sleep 设计理念,导致 Thread.currentThread().isInterrupted() 标记位会被清除,

所以才会导致程序不能退出。

这里如果要停止线程,只需要在 catch 内 再调用一次 interrupt(); 方法

try {
    // 每次循环都要等待10毫秒,模拟阻塞
    Thread.sleep(10);
} catch (InterruptedException e) {
    e.printStackTrace();
    Thread.currentThread().interrupt();
}
复制代码

所以说,不要以为调用了 interrupt() 方法,线程就一定会停止。

两种停止线程最佳方法

1. 捕获了 InterruptedException 之后的优先选择:在方法签名中抛出异常

public class RightWayStopThreadInProd implements Runnable {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadInProd());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }

    @Override
    public void run() {
        while (true) {
            System.out.println("go...");
            try {
                throwInMethod();
            } catch (InterruptedException e) {
                // 捕获异常,进行保存日志、停止程序等操作
                System.out.println("stop");
                e.printStackTrace();
            }
        }
    }

    /**
     * 如果方法内要抛出异常,最好是将异常抛出去,由顶层的调用方去处理,而不是try/catch
     * 这样调用方才能捕获异常并作出其它操作
     * @throws InterruptedException
     */
    private void throwInMethod() throws InterruptedException {
        Thread.sleep(2000);
    }
}
复制代码

如果方法内要抛出异常,最好是将异常抛出去,由顶层的调用方去处理,而不是 try/catch

这样调用方才能捕获异常并做出其它操作。

2. 在 catch 中调用 Thread.currentThread().interrupt(); 来恢复设置中断状态

public class RightWayStopThreadInProd2 implements Runnable {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadInProd2());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }

    @Override
    public void run() {
        while (true) {
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("程序运行结束");
                break;
            }
            reInterrupt();
        }
    }

    private void reInterrupt() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }
}


 

### Java 中 `Thread.interrupt()` 方法的使用说明 `Thread.interrupt()` 是 Java线程编程中的一个重要方法,用于请求中断某个线程的操作。它通过设置目标线程的中断状态来通知线程停止其正在执行的任务[^1]。 #### 1. 设置中断标志 当调用 `thread.interrupt()` 时,如果目标线程处于运行状态,则会将其内部的中断标志位设置为 true。此时,可以通过 `Thread.currentThread().isInterrupted()` 或者 `Thread.isInterrupted()` 来检测此标志的状态[^2]。 #### 2. 阻塞状态下抛出异常 如果目标线程正处于阻塞状态(例如等待锁、休眠或者 I/O 操作),则调用 `interrupt()` 不仅会设置中断标志,还会立即终止这些阻塞操作,并抛出 `InterruptedException` 异常[^4]。需要注意的是,在这种情况下,中断标志会被自动重置为 false[^5]。 以下是具体的代码示例: ```java public class InterruptExample { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { for (int i = 0; i < Integer.MAX_VALUE; i++) { if (Thread.currentThread().isInterrupted()) { System.out.println("Thread interrupted, exiting..."); break; } try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 重新设置中断状态 System.out.println("Thread interrupted during sleep"); } System.out.println("Iteration: " + i); } }); thread.start(); // 让主线程稍作延迟后再发起中断 Thread.sleep(2000); // 发起中断请求 thread.interrupt(); // 等待子线程结束 thread.join(); } } ``` 在这个例子中,子线程在每次迭代之前都会检查自身的中断状态。如果发现已被标记为中断,则提前退出循环。另外,当线程因调用 `sleep()` 而进入阻塞状态时,一旦收到中断信号便会抛出 `InterruptedException` 并跳出阻塞。 #### 3. 清理资源与恢复中断状态 为了确保程序逻辑的一致性和健壮性,在捕获到 `InterruptedException` 后通常需要显式地恢复中断状态以便其他部分能够感知到这一事件的发生。这一步骤非常重要,尤其是在复杂的多层调用场景下可以避免遗漏处理潜在的中断情况。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值