一、线程生命周期
通过start方法启动线程,当run()方法执行完毕,线程生命周期即将终止
线程6种状态
- NEW:初始状态,线程被创建,还没调start方法
- RUNNING:运行状态,就绪ready和运行两种统一称为“运行中”
- WAITING:等待状态
- TIME_WAITING:超时等待状态,超市以后自动返回
- TERMINATED:终止状态,表示线程执行完毕
- BLOCKED:阻塞状态表示线程进入等待状态,也就是线程因为某些原因放弃了CPU的使用权,有如下几种情况
- 等待阻塞:运行线程调用了wait方法,jvm会把当前线程放入等待队列列
- 同步阻塞:当前线程获取对象同步锁时,锁已被其他线程占用,jvm会把当前线程放入锁池
- 其他阻塞:运行线程执行Thread.sleep或者t.join()方法,或是发出I/O请求,jvm会把当前线程设置为阻塞状态,当sleep结束、join线程终止、io处理完毕则线程恢复
线程状态查看
- 通过jps命令查看当前环境下运行的所有java进程pid
- 通过stack pid命令查询当前线程的状态。jstack是java虚拟机自带的一个堆栈跟踪工具,用于打印java进程ID,core file或远程调试服务的java堆栈信息。
二、线程启动原理
- 问题:为什么用start方法,而不直接执行run方法? 在JVM层面创建线程,并在线程的start里面调用了run方法
进入start方法源码,内部调用了native方法start0(),start0是在Thread静态块中注册的
registerNative方法定义在Thread.c文件中,其定义了各操作系统平台要用的关于线程的公共数据和操作。
参照地址Thread.c文件内容
在java虚拟机中,首先创建一个和平台相关的java线程,通过操作系统启动start方法,在该方法中最终会调用到run方法
jvm源码查看Hotspot源码
三、线程终止
- 问题:如何终止一个线程?
stop(),过时方法。使用该方法结束线程不会保证线程资源正常释放,可能出现一些不确定的状态
1、线程中断方法interrupt()
通过在线程外部调用interrupt方法,向线程发出中断信号,告诉线程可以中断了,但何时中断取决于线程自己。
线程通过自身的isInterrupted()判断,是否被中断。
eg.
public class InterruptDemo {
private static int i;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
// 默认情况isInterrupted返回false,通过thread.interrupt()变成true
while (!Thread.currentThread().isInterrupted()) {
i++;
}
System.out.println("Num: " + i);
}, "interruptDemo");
thread.start();
TimeUnit.SECONDS.sleep(1);
thread.interrupt(); // 加和不加的效果
}
}
通过标识位或中断操作,使线程终止前,有机会去清理资源,而不是武断的将线程停止,这种方式更优雅。
2、线程静态方法Thread.interrupted()
外面线程thread.interrupt(),设置中断标识,告诉线程可以终止了。
线程内部通过Thread.interrupted(),将线程标识状态复位。
eg.
public class InterruptDemo2 {
private static int i;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
while (true) {
if(Thread.currentThread().isInterrupted()) {
System.out.println("before: " +
Thread.currentThread().isInterrupted());
Thread.interrupted(); //对线程复位,由true—>false
System.out.println("after: " +
Thread.currentThread().isInterrupted());
}
}
}, "interruptDemo");
thread.start();
TimeUnit.SECONDS.sleep(1);
thread.interrupt();
}
}
3.其他情况复位(被动复位)
对于抛出InterruptedException异常的方法,JVM辉县把中断标识清除,然后才抛出InterruptedException异常,这个时候如果调用isInterrupted方法,会返回false
eg1.正常中断,状态为true
public class InterruptDemo3 {
private static int i;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
while (!Thread.currentThread().isInterrupted()) {
i++;
}
System.out.println("Num: " + i);
}, "interruptDemo");
thread.start();
TimeUnit.SECONDS.sleep(1);
thread.interrupt();
System.out.println("interrupt state: " + thread.isInterrupted());
}
}
eg2.中断抛异常,状态为false
public class InterruptDemo4 {
private static int i;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
while (!Thread.currentThread().isInterrupted()) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
System.out.println("Num: " + i);
}, "interruptDemo");
thread.start();
TimeUnit.SECONDS.sleep(1);
thread.interrupt();
System.out.println("interrupt state: " + thread.isInterrupted());
}
}
4.复位原因
复位:自身没有真正中断,所以线程内部会复位,什么时候中断取决于自身
两个作用:
(1)返回当前中断标识true
(2)对中断标识复位,表明响应过中断请求
5.中断原理
(1)interrupt方法内部原理
java源码会调用native方法interrupt0,在java虚拟机中会调用平台的interrupt方法,在文件os_*.cpp中,不同平台会使用不同的文件,因为java是跨平台的,最后通过set_interrupted()方法设置中断标识为true
(2)isInterrupted()
若中断标识为true,则先清除中断标识,也即通过set_interrupted()方法将中断标识还原,并抛出异常InterruptedException