一、线程的生命周期
线程的生命周期大致分为下面几种
1.新建状态
一个线程对象创建后,该线程就处于新建状态,处于新生状态的线程有自己的内存空间。
2.就绪状态
新建状态的线程通过start()进入就绪状态。
处于就绪状态的线程已经具备了运行条件,但还没有分配到CPU,处于线程就绪队列(尽管是采用队列形式,事实上,把它称为可运行池而不是可运行队列。因为cpu的调度不一定是按照先进先出的顺序来调度的),等待系统为其分配CPU。
3.运行状态
等待状态并不是执行状态,当系统选定一个等待执行的Thread对象后,它就会从等待执行状态进入执行状态,系统挑选的动作称之为“cpu调度”。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。
4.阻塞状态
处于运行状态的线程在某些情况下,让出CPU并暂时停止自己的运行,进入阻塞状态。
在阻塞状态的线程不能进入就绪队列,只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续运行。
以下情况会使线程进入阻塞状态:
- 线程调用sleep,暂时进入中断状态(不会释放持有的对象锁),时间到后等待系统分配CPU继续执行
- 线程调用一个阻塞式IO方法,在该方法返回之前,该线程被阻塞
- 试图获取一个对象的锁,但是该对象锁正被其他线程持有
- 程序调用了线程的suspend方法将线程挂起
- 线程调用wait,等待notify/notifyAll唤醒时(会释放持有的对象锁),注意这几个都是Object的方法
5.死亡状态
当线程的run()方法执行完,或者被强制性地终止(例如出现异常,或者调用了stop()、desyory()方法等等),就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。 如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
二、线程相关方法
Java提供了一些便捷的方法用于会线程状态的控制。
1.run & start
通过调用start启动线程,线程执行时会执行run方法的代码
2.sleep & yield
sleep-使当前线程暂停执行一段时间,但是并不会释放对象锁
yield-与sleep类似,只是不能由用户决定暂定多长时间,且只能让同优先级的线程有执行的机会
3.suspend & resume
suspend-线程进入阻塞状态,但不会释放锁。此方法已不推荐使用,因为同步时不会释放锁,会造成死锁的问题。
resume-使线程重新进入可执行状态
4.stop
不推荐使用,且以后可能去除,因为它不安全。它会解除由线程获取的所有锁定,当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,由此可能造成数据的不完整(后面的代码没执行完就停止了),很难检查出问题所在
5.join
把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。
如线程A中,调用线程B对象的join(线程B不能为创建状态),线程A会等待线程B执行完毕后才继续执行。
6.wait & notify/notifyAll
这三个都是Object类的方法
wait-使得线程进入阻塞状态,会释放持有的对象锁,使得其他线程可以访问需要该对象锁的同步方法or同步块
notify-选择一个锁对象上wait的线程唤醒,使这个唤醒的线程继续执行,其他线程仍然是wait状态
notifyAll-唤醒这个锁对象上wait的所有线程