一.线程中断
线程中断在多线程开发中非常重要。中断线程意味着在线程结束前停止当前的操作。停止线程有许多好处。比如资源的释放,当一个线程完成了任务时,停止该线程可以释放资源。错误处理,如果某个程序发生了错误,停止线程可以防止错误蔓延。性能够优化。停止不必要的线程可以提高程序的性能等。
一.使用标志位停止线程
public class Test4 {
public static volatile boolean IsStop = true;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
while (IsStop){
for(int i=0;i<100000;i++){
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
});
thread.start();
Thread.sleep(1000);
IsStop=false;
}
}
通过修改IsStop的值来达到停止线程的目的。这种写法是增加一个标志位,通过修改标志位的值来停止线程。试想一下如果把IsStop定义为局部变量还可以停止线程吗?
当我们把isStop变为局部变量,发现编译器报错,这是因为lambda表达式造成的。
通过对比发现lambda表达式想要修改变量这个变量必须是全局变量,这就是lambda的变量捕获。lambda表达式执行的时间比较晚。lanmba表达式看似是直接访问外部变量,其实是把外部的变量复制一份,当lambda表达式真正运行时,Lambda里面访问的局部变量可能会销毁,变量不存在,自然lambda表达式报错。要想解决这个问题让lambda表达式访问的变量是全局变量,或者是被final修饰的局部变量。
二.interrupt
手动标志位的方法太麻烦,java提供了2个类用来判断当前线程状态是否停止,interrupted,IsInterrupted。
一.interrupt
点开源码发现interrupted的返回值是boolean,其功能是判断当前线程是否中断。interrupted有清除标志位的特点,当连续调用2次interrupted,第一次返回中断线程true,第二次调用的时候由于第一次清除了标志位,所以第二次的返回值是false。
public class Test5 {
public static void main(String[] args) {
Thread thread = new Thread(()->{
while (!Thread.currentThread().isInterrupted()) {
System.out.println("hello");
}
});
thread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
thread.interrupt();
}
}
main线程运行到thread.intrrupt时,正确中断线程会返回true,Thread.currentThread().isInterrupted()会接收一个true,!Thread.currentThread().isInterrupted()会把true变为false,让循环停止。
public class Test5 {
public static void main(String[] args) {
Thread thread = new Thread(()->{
while (!Thread.currentThread().isInterrupted()) {
System.out.println("hello");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
thread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
thread.interrupt();
}
}
通过给线程加上sleep语句,发现编译器报错。这是因为当main线程执行到thread.intrupt的时候,恰巧thread运行到到sleep,线程正在休眠,intrupt会唤醒该线程。也就是说线程本来应该休眠1000毫秒,恰好在第10毫秒的时候,main线程运行到intrrupt,会让sleep停止休眠。这样做的好处是让程序员有更多的操作空间,让程序是否继续当前操作由程序员决定。同时sleep操作也会清除标志位从而抛出异常。要想让程序结束只需要加个break语句跳出当前循环。
public static void main(String[] args) {
Thread thread = new Thread(()->{
while (!Thread.currentThread().isInterrupted()) {
System.out.println("hello");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
});
thread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
thread.interrupt();
}
}
当程序让sleep抛出异常时,直接跳出循环从而让程序结束。
二.isinterrupt
isinterrupted和interrupted的返回值一样,interrupt判断当前线程是否中断,isterrupt判断线程是否中断。
public class Test5 {
public static void main(String[] args) {
Thread thread = new Thread(()->{
while (!Thread.currentThread().isInterrupted()){
System.out.println("hello");
}
});
thread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("第一次调用"+thread.isInterrupted());
thread.interrupt();
System.out.println("第二次调用"+thread.isInterrupted());
}
}
第一次调用isinterrupted线程还没停止返回flase,第二次调用isinterrupted线程停止返回true.