1.线程的概念
线程:系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。线程——程序执行的最小单位。
2.1线程的状态,java线程在运行中的生命周期中可能会处于6种不同的状态。
- New(new)新创建状态。线程被创建,还没有调用start方法之前,在线程运行之前还要初始化一些基础工作要做。
- Runnable(runnable)可运行状态。线程一旦调用start方法,线程就处于Runnable状态,一个可运行的线程可能是正在运行也可能没有运行,这取决于操作系统给线程提供运行的时间。
- Blocked(blocked)阻塞状态。表示该线程被锁阻塞,它暂时不活动。
- Waiting(waiting)等待状态。线程暂时不活动,并且不运行任何代码,这消耗最少的资源,直到线程调度器重新激活它。
- Timed waiting:超时等待状态。和等待状态不不同的是,它是可以在指定的时间自行返回。
- Terminated:终止状态。表示当前线程竟已经执行完毕,导致线程终止两种情况,第一种就是run方法执行完毕正常退出,第二种就是一个没有捕获的异常而终止了run方法,导致线程进入终止状态。
2.2线程运行状态(这个张图必须的会画)
线程创建后,调用 Thread 的 start 方法,开始进入运行状态,当线程执行wait 方法后,线程进入等待状态,进入等待状态的线程需要其他线程通知才能返回运行状态。超时等待相当于在等待状 态加上了时间限制,如果超过时间限制,则线程返回运行状态。当线程调用到同步方法时,如果线程没有获得锁则进入阻塞状态,当阻塞状态的线程获取到锁时则重新回到运行状态。当线程执行完毕或者遇到意外异常终止时,都会进入终止状态。
3.线程中断(理解中断)
当线程的run方法执行完毕,或者在方法中出现没有捕获的异常时,线程将终止,在java早期版本中有一个stop()方法,其他的线程可以调用它终止线程,但是这个方法现在已经被弃用了。interrupt方法可以用来请求中断线程,当一个线程调用interrupt方法时,线程的中断标识位将被置位(中断标识位为true),线程将会不时地检测这个中断标识位,以判断线程是否应该被中断,。要想知道线程是否被置位,可以调用Thread.currentThread().isInterrupted(), 如下所示:
while(!Thread,currentThread().isInterrupted()){
//todo 代码逻辑
}
还可以调用Thread.interrupted()来对中断标识位进行复位。但是如果一个线程被阻塞,就无法检测中断状态,如果一个线程处于阻塞状态,线程在检查中断标识位时如果发现中断标识位为true,则会在阻塞方 法调用处抛出InterruptedException异常,并且在抛出异常前将线程的中断标识位复位,即重新设置为 false。 需要注意的是被中断的线程不一定会终止,中断线程是为了引起线程的注意,被中断的线程可以决定如何 去响应中断。如果是比较重要的线程则不会理会中断,而大部分情况则是线程会将中断作为一个终止的请 求。另外,不要在底层代码里捕获InterruptedException异常后不做处理,如下所示:
如果你不知道抛出InterruptedException异常后如何处理,这里介绍两种合理的处理方式。 (1)在catch子句中,调用Thread.currentThread.interrupt()来设置中断状态(因为抛出异常后中断标 识位会复位),让外界通过判断Thread.currentThread().isInterrupted()来决定是否终止线程还是继续下 去,应该这样做:
(2)更好的做法就是,不使用try来捕获这样的异常,让方法直接抛出,这样调用者可以捕获这个异 常,如下所示:
4.安全地终止线程
前面我们讲到了中断,那么首先就用中断来终止线程,代码如下:
在上面代码注释1处调用了sleep方法使得main线程睡眠10ms,这是为了留给MoonThread线程时间来感 知中断从而结束。除了中断,还可以采用boolean变量来控制是否需要停止线程,代码如下:
在上面代码注释1处用到了volatile关键字,volatile在这里我们只需要知道因为 涉及多个线程对这个变量的访问,所以当我们在设置volatile boolean on的时候,当有其他线程改变其值时, 所有的线程都会感知到它的变化。这两种方式输出的结果类似,如图所示。这里只打出了部分信息,i的 值由1一直打印到1390(输出的结果是变化的)。最后打印stop,说明执行到了run方法的末尾,线程即将终 止。