interrupt 停止线程
对于 Java 而言,最正确的停止线程的方式是使用 thread.interrupt();
。Java 并不提供强制停止线程的能力。原因是:贸然强制停止线程可能会造成一些安全的问题。
java
while (!Thread.currentThread().isInterrupted() && more work to do) {
do more work
}
sleep 期间能否感受到中断
java
public class StopDuringSleep {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int num = 0;
try {
while (!Thread.currentThread().isInterrupted() && num <= 1000) {
System.out.println(num);
num++;
Thread.sleep(1000000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(5);
thread.interrupt();
}
}
如果线程在执行任务期间有休眠需求,主线程休眠 5 毫秒后,通知子线程中断,此时子线程正在执行 sleep 命令。 上面情况会有下面结果:
如果 sleep、wait 等让线程进入休眠状态,处于休眠状态的线程被中断,线程是可以感受到中断信号的,并且会抛出 InterruptedException
异常,同时清除中断信号,将中断标记设置为 false。
所以最佳处理方式是方法签名抛出中断异常,将中断信号向上传递。
volatile 标记位停止线程是错误的
top()
,suspend()
和 resume()
,这些方法已经被 Java 直接标记为 @Deprecated
。原因如下:
stop()
方法会直接把线程停止,会导致数据完整性等问题。suspend()
方法不会释放锁 ,这把锁在线程被resume()
之前是不会被释放的, 容易导致死锁问题。
volatile 适用的场景
java
public class VolatileCanStop implements Runnable {
private volatile boolean canceled = false;
@Override
public void run() {
int num = 0;
try {
while (!canceled && num <= 1000000) {
if (num % 10 == 0) {
System.out.println(num + "是10的倍数。");
}
num++;
Thread.sleep(1);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
VolatileCanStop r = new VolatileCanStop();
Thread thread = new Thread(r);
thread.start();
Thread.sleep(3000);
r.canceled = true;
}
}
上面在主线程设置 volatile
为 true 后,子线程就停止了。
volatile 修饰标记位不适用的场景
java
class Producer implements Runnable {
public volatile boolean canceled = false;
BlockingQueue storage;
public Producer(BlockingQueue storage) {
this.storage = storage;
}
@Override
public void run() {
int num = 0;
try {
while (num <= 100000 && !canceled) {
if (num % 50 == 0) {
// 假设这里阻塞了
storage.put(num);
System.out.println(num + "是50的倍数,被放到仓库中了。");
}
num++;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("生产者结束运行");
}
}
}
假设子线程在 storage.put(num);
时被阻塞了,这时主线程设置 volatile
变量为 true,子线程没办法进入下一次循环判断,所以并不会停止。但是如果使用 interrupt
方法中断,子线程就可以感受到中断信号,做出响应。