1、生命周期
生命周期有广义与狭义之分,狭义为生命科学术语,指包括人类在内的一切动物由出生到死亡经历的生命全程。广义的生命周期泛指自然界与人类社会各种客观事物的阶段性变化及规律,如家庭生命周期、产品生命周期等。本义即狭义的生命周期,专指人类的生命周期。
2、线程的生命周期
就是线程从生到死的过程中,经历的各种状态及状态转换。
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5 种状态。尤其是当线程启动以后,它不可能一直"霸占"着 CPU 独自运行,所以CPU 需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换。
2.1、New(初始化状态)
用new语句创建的线程处于新建状态,此时它和其他Java对象一样,仅仅在堆区中被分配了内存。如:Thread t = new MyThread();
2.2、Runnable(就绪状态)
当调用线程对象的start()方法,线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,Java虚拟机会为它创建方法调用栈和程序计数器。处于这个状态的线程位于可运行池中,等待获得CPU的使用权,并不是说执行了start()此线程立即就会执行。
2.3、Running(运行状态)
当就绪状态中的线程获得了CUP执行资源,执行run()中的代码,这样的线程我们称为运行状态的线程。
2.4、Blocked(阻塞状态)
处于运行中的线程,由于某种原因放弃对cpu的使用权,处于阻塞状态,直到其进入就绪状态,才有机会再次被cpu调用进入运行状态。
根据阻塞原因不同,阻塞分为三种
:
-
等待阻塞:运行状态中的线程执行wait方法,进入等待队列,等待阻塞;Java虚拟机就会把线程放到这个对象的等待池中;
-
同步阻塞:线程获取同步锁失败(因为锁被其他线程占用),Java虚拟机就会把这个线程放到这个对象的锁池中;
-
其他阻塞:通过调用sleep方法或者join方法或者发出I/O请求时,线程会进入阻塞状态,当sleep()状态超时,或者join()等待线程终止或者超时,或者I/O处理完毕,线程重新转入就绪状态;
2.5、Terminated(终止状态)
-
正常结束,线程执行完
-
异常退出
异常退出,除了程序有问题导致的异常的退出,还可以使用共享变量的方式(定义个boolean标识等)退出,或者Interrupt中断线程,抛出异常,捕获异常break,跳出循环状态; -
调用stop(),会造成死锁,线程不安全,不建议使用
3、 java中对线程定义了6种状态
- NEW :创建了 Thread 对象,但是还没调用 start() 方法,系统内核里还没有线程。
- RUNNABLE:就绪状态(1.正在 CPU 上运行;2.还没在 CPU 运行,但是已经准备好了)
- BLOCKED:阻塞状态(等待锁)
- WAITING:阻塞状态(线程中调用了 wait() )
- TIMED_WAITING:阻塞状态(线程中通过 sleep 进入的阻塞状态)
- TERMINATED:系统里面的线程已经执行完毕,销毁了,但是 Thread 对象还在。
1)新建状态(NEW): 当我们创建一个新的Thread对象时,该线程就处于新建状态,例如:Thread t = new Thread();
2)可运行状态(RUNNABLE): 当线程对象调用start()方法后,线程进入可运行状态。在这个状态下,线程已经做好了准备,随时等待CPU调度执行,这个状态包括了"就绪"和"运行"状态。
3)阻塞状态(BLOCKED): 线程在等待获取一个锁以进入或重新进入同步代码块时,它会进入阻塞状态。只有当该锁被释放并且线程被调度去获取这个锁,线程才能转换到RUNNABLE状态。
4)等待状态(WAITING): 线程进入等待状态,是因为它调用了其它线程的join方法,或者调用了无参数的wait方法。在这种情况下,线程会等待另一个线程的操作完成或者等待notify/notifyAll消息。
5)定时等待状态(TIMED_WAITING): 线程进入定时等待状态,是因为它调用了sleep或者带有指定时间的wait或join方法。在指定的时间过去之后,线程会自动返回RUNNABLE状态。如果它是由于调用wait或join方法进入的定时等待状态,还需要等待notify/notifyAll消息或者等待join的线程终止。
6)终止状态(TERMINATED): 线程任务执行完毕或者由于异常而结束,线程就会进入终止状态。在这个状态下,线程的生命周期实际上已经结束了,它不能再转换到其他任何状态。
3.1、状态转换图
3.2、状态转换说明
3.2.1、NEW -> RUNNABLE
当通过new Thread()方法创建一个新线程时,该线程处于NEW状态。此时如果调用start()方法启动该线程,该线程就会进入RUNNABLE状态,并被加入JVM的线程调度队列中。
3.2.2、RUNNABLE -> BLOCKED
当一个线程正在获取同步锁时,如果该锁已经被其他线程占用,则该线程将进入BLOCKED状态,等待获取锁以便进入同步区域执行。此时的线程并不会占用CPU资源。
3.2.3、RUNNABLE -> WAITING
当一个线程通过wait()方法进入等待状态时,该线程将进入WAITING状态,并且释放其持有的锁。在该状态下,该线程会暂停执行直到其他线程发出notify()或notifyAll()方法通知它继续执行。
3.2.3.2.4、RUNNABLE -> TIMED_WAITING
当一个线程通过sleep(millis)、join(millis) 或 wait(millis) 方法进入有时限的等待状态时,该线程将进入TIMED_WAITING状态,并且暂停执行一段时间后被自动唤醒,然后再次参与竞争CPU资源。
5、BLOCKED / WAITING / TIMED_WAITING -> RUNNABLE
当当前拥有同步锁的线程释放锁时,将会从BLOCKED状态转换为RUNNABLE状态。当其他线程通过notify()或者notifyAll()方法通知等待某个条件的线程时,该线程将从WAITING或TIMED_WAITING状态转变为RUNNABLE状态。
3.2.6、RUNNABLE -> TERMINATED
线程执行完毕后或线程抛出一个未捕获的异常时,该线程都将进入TERMINATED状态。