在Java并发设计中,多线程是一个不可或缺的点。那么下面我们就来看一下线程的生命周期
线程的生命周期
线程的被创建启动之后并不会直接就进入执行状态,而是要经过以下5中状态:
新建(New)——>就绪(Runnable)——>运行(Running)——>阻塞(Blocking)——>死亡(Dead)
需要注意的是当线程启动之后CPU并不是在一个线程上一直执行下去,而是通过分片为每个线程分配执行时间以此达到多线程共同执行的目的,所以线程也会在执行和阻塞之间来回切换。
-
新建
当程序使用new关键字创建出一个线程后,该线程就处于新建状态,此时由JVM分配对象的地址空间并初始化其成员变量的值; -
就绪
当调用Thread.start()之后线程处于就绪状态,Java虚拟机会为其创建方法调用栈和程序计数器,等待CPU调度; -
运行
处于就绪状态的线程被CPU执行之后就处于运行状态; -
阻塞
当线程因为某种原因放弃了对CPU的使用权,让出了CPU的cpu timeslice,暂时停止运行。直到线程重新进入就绪状态,才有机会继续获得CPU使用权运行线程。
线程阻塞的原因大概分为三种情况:
Ⅰ.一般线程中的阻塞
A.线程执行Thread.sleep()方法;
B.线程执行同步代码块,但是尚且无法获得相关的锁,只能阻塞等待其他线程释放锁之后获得该同步锁。
C.执行了一个对象的wait()方法,直接进入阻塞;
D.线程执行某些I/O操作时,为了等待相关资源而进入阻塞状态;
Ⅱ.Socket客户端的阻塞
A.请求与服务器连接时,调用connect()方法,进入阻塞状态,直到连接成功;
B.当从Socket输入流读取数据时候,在读取足够的数据之前进入阻塞状态;
C.调用Socket的setSoLinger()方法时关闭了Socket延迟,当执行Socket的close方法时,会进入阻塞状态;
Ⅲ.Socket服务器的阻塞
A.线程执行ServerSocket的accept()方法,等待客户端连接,直到接收到连接之前都处于阻塞状态;
B.从Scoket输入流读取数据时,如果输入流没有足够的数据,就会阻塞;
C.线程向Scoket的输出流写入一批数据时会阻塞;
Java中常见的使线程阻塞的方法:
- sleep() :允许以毫秒为单位在指定一段时间内让线程进入阻塞状态;
- suspend()和resume() :两个方法配套使用,suspend()使线程进入阻塞状态,并且不会自动恢复,必须使用对应的resume()方法才能使线程重新回到就绪状态;
- wait()和notify()/notifyAll() :两个方法配套使用,wait()使得线程进入阻塞状态,他有两种形式,一种是允许 指定毫秒为单位的一段时间和没有参数,前者对应的notify()被调用或者超出指定时间线程重新回到就绪状态,后者只能调用notify()/notifyAll()方法;
-
死亡
线程死亡的方式有以下三种:
A.线程正常运行结束;
B.使用退出标志退出;
C.interrupt()方法退出;调用interrupt()方法来中断线程的情况:
1.当线程正处于中断状态(sleep,wait,suspend等),当调用interrupt()时会抛出InterruptExecption()异常,通过代码进行异常捕获,随即可以使用break跳出线程;
2.线程处于正常状态时,使用isInterrupted()判断线程的中断标志来退出循环;D.额外说明的是,程序也可以直接使用stop()方法来中断线程,但是这样做很不安全,不能保证数据的一致性,所以此方法已经废弃;