我们知道Java中线程在运行完run()方法之后就会停止,这样的情况是自然停止,那如果我们在run()方法运行的时候想停止线程该怎么做呢?
stop()方法存在的弊端,如下边的例子
public class InterruptedDemo{
public static void main(String[] args) {
StopDemo demo = new StopDemo();
demo.start();
try {
//阻塞1秒,保证i自增完毕
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
demo.stop();//停止线程
demo.sout();//输出i、j自增结果
}
private static class StopDemo extends Thread{
//定义两个变量在run()方法中做自增操作
private int i = 0;
private int j = 0;
@Override
public void run() {
i++;
try {
//阻塞十秒钟
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
j++;
}
//输出i/j自增结果
public void sout(){
System.out.println("i="+i+",j="+j);
}
}
}
输出的结果为
i=1,j=0
从上面的例子看出,如果在线程没有执行完毕就使用stop()方法强制停止线程就有可能导致数据的不一致性,而且Thread.stop()方法现在已经被弃用
Interrupted是针对run()方法中存在无限循环或者线程阻塞做出的一个线程中断的优化方案
无限循环优化
Interrupt案例,如下
public class InterruptedDemo implements Runnable{
@Override
public void run() {
while (true){
}
}
public static void main(String[] args) {
Thread t = new Thread(new InterruptedDemo());
t.start();
try {
t.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
t.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在上边例子中的run()方法中使用了一个while(true)使得线程无限循环下去,但是这样子做的话会使得该线程一直占用CPU资源,如果不需要该线程执行下去是没有办法停止的,除非我们在控制台上直接kill掉,我们可以做一个优化,如下
volatile boolean interrupted = false;
@Override
public void run() {
while (!interrupted){
}
}
在这里设置了一个可见变量,当我们不需要该线程一直运行的话只需要将interrupt设置为true,线程就跳出了循环,线程自然停止
在Java中提供了一个方法Thread.currentThread().isInterrupted()
该方法可以提供一个共享的线程中断的标记,默认值为false;我们可以该方法来代替我们自定义的可见变量
public class InterruptedDemo implements Runnable{
int i = 0;
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){
System.out.println("test:"+i++);
}
}
public static void main(String[] args) {
Thread t = new Thread(new InterruptedDemo());
t.start();
t.interrupt();//将Thread.currentThread().isInterrupted()设置为true从而停止线程
}
}
线程阻塞优化
在上面Interrupt案例中的main方法中我们看到当调用wait、sleep、join等方法都会抛出一个InterruptedException
异常,我们先做一个假设:如果调用wait、sleep、join等方法没有抛出任何异常的情况下,如果我们想中断异常即使调用了interrupt
方法也只能到线程阻塞时间达到超时时间之后再执行完run()方法自然终止线程,假定超时时间有24小时,那线程也只能等24小时之后才可以中断。这样的话就没有意义了;看下边例子
public class InterruptedDemo implements Runnable {
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()){
//当这里阻塞了这么多秒时下边main方法调用interrupt方法
// 将Thread.currentThread().isInterrupted()的值改为true也得等到线程唤醒以后才会执行完毕
TimeUnit.SECONDS.sleep(100000000);
}
}
public static void main(String[] args) {
InterruptedDemo demo = new InterruptedDemo();
Thread t = new Thread(demo);
t.start();
t.interrupt();
}
}
当抛出了InterruptedException
异常又是怎么处理的呢?看下边代码
public class InterruptedDemo implements Runnable {
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()){
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {//在这里InterruptedException触发了线程复位,也就是将Thread.currentThread().isInterrupted()的值改为false
e.printStackTrace();
//在这里我们可以决定线程是否中断
}
}
System.out.println("Interrupted run");
}
public static void main(String[] args) throws InterruptedException {
InterruptedDemo demo = new InterruptedDemo();
Thread t = new Thread(demo);
t.start();
t.sleep(1000);//这里让main线程阻塞1秒,保证InterruptedException有足够的时间响应中断
t.interrupt();
}
}
输出结果
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at com.thread.InterruptedDemo.run(InterruptedDemo.java:12)
at java.lang.Thread.run(Thread.java:748)
上面代码中我们看到当main
方法中t
调用了interrupt
方法后将Thread.currentThread().isInterrupted()
的值改为true
,但是线程并没有被中断而且也没有输出Interrupted run
,只是报了一个异常。那是因为InterruptedException
将线程进行了复位(将true
又变回了false
);这样做的目的就是为了将线程的中断权限交给线程自己,由它自己来决定线程是否中断。因为main
线程是不知道异步线程的运行的状态。将中断权限交给线程自己去处理,这样就达到了友好中断的目的
当线程复位后我们一定要在catch中去做处理,否则我们的中断就没有任何意义了
调用Interrupt都做了什么:
Thread.currentThread().isInterrupted()
的值变为trueThread.interruped()
在这里有关于此问题不懂的朋友欢迎给我留言
建议先看一下下边两个文章会更好的理解
java线程的声明周期
java创建线程的三种方式