多线程学习二,线程生命周期和线程中断
1. Java线程生命周期
思维导图:
示意图:
2. 线程中断
思维导图:
在java的世界里,Thread类是对线程概念的抽象。想要中断一个线程有两种方式:
- (1)调用Thread类的stop方法
- (2)组合调用Thread类的
interrupt
、interrupted
和isInterrupted
方法
通过源码的注释我们可以知道,调用stop()可能会产生线程异常,抛出SecurityException
异常。其原因为使用stop
方法进行中断线程本质上是不安全的,它会直接释放掉本线程所持有的所有资源,举个简单的栗子来说,假如我们正在使用某个线程下载电影,如果该线程或者其他线程通过该线程的stop进行中断,则原来下载的内容将全部丢失。
由此我们可以知道Java线程的设计是协作式
的,而不是抢占式
的,
2.1 interrupt()实现中断
interrupted()方法是发起线程中断请求,但只是请求,并不会真的把线程给中断,实际上是把线程的中断标识设置为了true
public static void main(String[] args) {
//通过interrupted() 方法检测线程是否被中断
System.out.println(Thread.currentThread().getName()+"线程是否中断"+Thread.interrupted());
//设置线程中断
Thread.currentThread().interrupt();
//通过interrupted()方法检测线程是否被中断,并且会恢复中断
System.out.println(Thread.currentThread().getName()+"线程是否中断"+Thread.interrupted());
}
输出:
main线程是否中断false
main线程是否中断true
2.2 isInterrupted()检查线程中断
isInterrupted()方法是判断线程的中断标识是否为true
public static void main(String[] args) {
//当前线程
Thread thread = Thread.currentThread();
//检测当前线程时候被中断
System.out.println(thread.getName()+"线程是否中断"+thread.isInterrupted());
//设置当前线程的中断标识
thread.interrupt();
//检测当前线程是否中断
System.out.println(thread.getName()+"线程是否中断"+thread.isInterrupted());
//检测当前线程中断状态是否被清除
System.out.println(thread.getName()+"线程是否中断"+thread.isInterrupted());
try {
//线程休眠2s
//这里设置的是线程休眠20s,但是因为线程中断,所有sleep会被打断
Thread.sleep(20000);
System.out.println(thread.getName()+"线程休眠未被中断...");
} catch (Exception e) {
System.out.println(thread.getName()+"线程休眠被中断...");
//判断线程是都被中断
System.out.println(thread.getName()+"线程是否中断"+thread.isInterrupted());
}finally {
System.out.println(thread.getName()+"线程是否中断"+thread.isInterrupted());
}
}
输出结果:
main线程是否中断false
main线程是否中断true
main线程是否中断true
main线程休眠被中断...sleep interrupted
main线程是否中断false
main线程是否中断false
在这个栗子中我们看到线程在设置interrupt()
后,会继续往下执行,并没有马上中断,这其实只是发起线程中断请求,并不会真的把线程给中断,只是把线程的中断标识设置为了true。CPU在执行线程时又没有阻塞也没有转到其他语句,当然要往下执行,interrupt()
只是抛出一个消息,“要求”调用他的进程中断,他只是负责发出要求,具体执行由其他函数实现
。所以这个语句出现进程不会中断。
🚩在线程执行到Thread.sleep(20000)
时,线程由运行态
转换为阻塞态
,但是因为设置了线程中断,所以sleep()
会执行终端,线程会退出阻塞态继续执行,由此可以我们可以知道interrupt()
是中断处于阻塞/睡眠状态的线程
,需要注意的是这里的中断并不是让线程死亡, 而是退出阻塞状态继续运行,所以interrupt不能中断运行中的线程。
jdk里interrupt()
解释:
当然 Java 也有很多底层实现是看不到的:
2.3 interrupted检测线程中断并清除中断
没有栗子,可以看2.1的栗子
2.4 线程中断异常处理
在2.2中我们可以知道,当线程阻塞的调用一些方法会导致InterruptedException
异常抛出
在捕获异常的时候我们也可以对其进行异常处理
- 抛出
InterruptedException
- 捕获
InterruptedException
后重新抛出。 - 检测到中断后,重新设置线程中断.
//捕获InterruptedException后重新抛出。
public static void main(String[] args) throws InterruptedException {
//当前线程
Thread thread = Thread.currentThread();
try {
//设置当前线程的中断标识
thread.interrupt();
Thread.sleep(20000);
} catch (InterruptedException e) {
System.out.println(thread.getName()+"线程休眠被中断..."+e.getMessage());
//判断线程是都被中断
System.out.println(thread.getName()+"执行终端处理");
throw e;
}
输出:
main线程休眠被中断...sleep interrupted
main执行终端处理
Exception in thread "main" java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.fx.multiThread._7_HandleInterruptedException.main(_7_HandleInterruptedException.java:15)
//检测到中断后,重新设置线程中断.
public class _8_ReInterrupted extends Thread{
public static void main(String[] args) throws InterruptedException {
//当前线程main
String threadName = Thread.currentThread().getName();
_8_ReInterrupted reInterrupted = new _8_ReInterrupted();
System.out.println(printDate() + threadName + " 线程启动");
//启动新线程
reInterrupted.start();
//主线程休眠3秒
Thread.sleep(3000);
System.out.println(printDate() + threadName + " 设置子线程中断");
//对子线程设置线程中断 在主线程里面操作其他线程
reInterrupted.interrupt();
//主线程休眠3秒
Thread.sleep(3000);
System.out.println(printDate() + threadName + " 运行结束");
}
@Override
public void run() {
//当前线程
String threadName = Thread.currentThread().getName();
int i = 0;
//循环等待线程中断
while (!Thread.currentThread().isInterrupted()){//只判断状态不清除状态
System.out.println(printDate() + threadName + " 线程正在执行第:"+(++i)+"次");
try {
//线程阻塞,如果线程收到中断操作型号将抛出异常
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(printDate() + threadName + " 线程抛出异常");
//检测线程是否中断
System.out.println(printDate() + threadName + " 线程是否被中断" + this.isInterrupted());
//TODO 如果不需要则不用调用 如果调用Interrupt()方法的话,则当前线程状态变为中断,将退出循环,程序结束
Thread.currentThread().interrupt();
}
}
System.out.println(printDate() + threadName + " 线程是否被中断" + this.isInterrupted());
System.out.println(printDate() + threadName + " 线程退出");
}
//打印当前事件
private static String printDate() {
return new SimpleDateFormat("HH:mm:ss").format(System.currentTimeMillis())+"\t";
}
}