线程从建立、运行到结束老是处于下面五个状态之一:初始状态、就绪状态、运行状态、阻塞状态及死亡状态。spa
-
线程的状态图
(1) 初始状态
实现Runnable接口和继承Thread能够获得一个线程类,new一个实例出来,线程就进入了初始状态。线程
(2) 就绪状态
一个新建立的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法建立线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。对象
处于就绪状态的线程并不必定当即运行run()方法,线程还必须同其余线程竞争CPU时间,只有得到CPU时间才能够运行线程。由于在单CPU的计算机系统中,blog
不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。所以此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序来调度的。继承
(3) 运行状态(running)
当线程得到CPU时间后,它才进入运行状态,真正开始执行run()方法。接口
(4) 阻塞状态(blocked)
线程运行过程当中,可能因为各类缘由进入阻塞状态:队列
①线程经过调用sleep方法进入睡眠状态;资源
②线程调用一个在I/O上被阻塞的操做,即该操做在输入输出操做完成以前不会返回到它的调用者;同步
③线程试图获得一个锁,而该锁正被其余线程持有;it
④线程在等待某个触发条件;
所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其余处于就绪状态的线程就能够得到CPU时间,进入运行状态。
(5) 死亡状态(dead)
有两个缘由会致使线程死亡:
①run方法正常退出而天然死亡;
②一个未捕获的异常终止了run方法而使线程猝死;
为了肯定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),须要使用isAlive方法,若是是可运行或被阻塞,这个方法返回true;
若是线程仍旧是new状态且不是可运行的,或者线程死亡了,则返回false。
(6)等待队列(本是Object里的方法,但影响了线程)
- 调用obj的wait(), notify()方法前,必须得到obj锁,也就是必须写在synchronized(obj) 代码段内。
- 与等待队列相关的步骤和图
- 线程1获取对象A的锁,正在使用对象A。
- 线程1调用对象A的wait()方法。
- 线程1释放对象A的锁,并立刻进入等待队列。
- 锁池里面的对象争抢对象A的锁。
- 线程5得到对象A的锁,进入synchronized块,使用对象A。
- 线程5调用对象A的notifyAll()方法,唤醒全部线程,全部线程进入锁池。||||| 线程5调用对象A的notify()方法,唤醒一个线程,不知道会唤醒谁,被唤醒的那个线程进入锁池。
- notifyAll()方法所在synchronized结束,线程5释放对象A的锁。
- 锁池里面的线程争抢对象锁,但线程1何时能抢到就不知道了。||||| 本来锁池+第6步被唤醒的线程一块儿争抢对象锁。
(7)锁池状态
- 当前线程想调用对象A的同步方法时,发现对象A的锁被别的线程占有,此时当前线程进入锁池状态。简言之,锁池里面放的都是想争夺对象锁的线程。
- 当一个线程1被另一个线程2唤醒时,1线程进入锁池状态,去争夺对象锁。
- 锁池是在同步的环境下才有的概念,一个对象对应一个锁池。
(8)几个方法的比较
- Thread.sleep(long millis),必定是当前线程调用此方法,当前线程进入阻塞,但不释放对象锁,millis后线程自动苏醒进入可运行状态。做用:给其它线程执行机会的最佳方式。
- Thread.yield(),必定是当前线程调用此方法,当前线程放弃获取的cpu时间片,由运行状态变会可运行状态,让OS再次选择线程。做用:让相同优先级的线程轮流执行,但并不保证必定会轮流执行。实际中没法保证yield()达到让步目的,由于让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会致使阻塞。
- t.join()/t.join(long millis),当前线程里调用其它线程1的join方法,当前线程阻塞,但不释放对象锁,直到线程1执行完毕或者millis时间到,当前线程进入可运行状态。
- obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout)timeout时间到自动唤醒。
- obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的全部线程。