复习
讲java线程的状态切换前先复习下线程创建的几种方法
- 继承thread类
- 实现runnable接口
- 使用Callable和Future
- 使用线程池
介绍
java线程有五个状态:初始、就绪、运行、阻塞、死亡。
状态之间的切换用文字描述比较繁琐,千言不如一图,参考网友整理的图:https://www.iteye.com/blog/zy19982004-1626916
五种状态
- 初始(new)
- new一个线程实例,就进入线程初始态
- 就绪(start)
- 线程拿到对象锁:运行-》锁池队列-》就绪,运行-》等待队列-》锁池队列-》就绪
- 线程调用yield()方法:运行-》就绪
- 等待用户输入完毕:阻塞-》就绪
- 其他线程join()结束:阻塞-》就绪
- 当前线程sleep()方法结束:阻塞-》就绪
- 运行(run)
- 线程调度程序从就绪的线程中选择一个作为当前线程时线程所处的状态(唯一一种方式):就绪-》运行
- 阻塞(sleep,yield,suspend)
- 等待阻塞——进入等待队列:运行状态中的线程执行wait()方法:运行状态-》等待队列
- 同步阻塞——进入锁池队列:
- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用):运行状态-》锁池队列
- 等待队列中的线程被notify或者notifyall唤醒:等待队列-》锁池队列
- 其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时:运行-》阻塞
- 死亡
- 当线程执行完run()方法:运行-》死亡
- 主线程执行完main()方法:运行-》死亡
从图和上面的总结可以看出
- 线程进入绪状态的情况主要有三种:a.阻塞-》就绪,b.运行-》就绪,c.线程锁池-》就绪
- 进入锁池队列的情况有两种:a.运行-》锁池队列,b.等待队列-》锁池队列
问题
1. start和run区别
- 调用start方法,是在当前线程启动一个新线程;调用run方法,是在当前线程执行一个run方法,实际并没有产生新线程
- 调用start方法,线程从初始状态进入就绪状态;调用run方法,线程从就绪状态进入运行状态
- 调用start方法,新线程获得CPU时间片就开始执行;调用run方法,线程从就绪状态进入运行状态
2. sleep和wait区别
- 线程执行sleep和wait都会让出cpu使用权
- 执行sleep时不会释放对象锁;执行wait时会释放所有资源,包括对象锁
- 执行sleep线程从运行态进入阻塞态;执行wait从运行态进入等待队列
- sleep等时间到了从阻塞态进入就绪态;wait依靠notify或notifyall唤醒,从等待队列进入锁池队列
- wait() 和 notify() 方法必须在 synchronized方法或块中调用,因为只有在synchronized方法或块中当前线程才占有锁,才有锁可以释放,否则在运行时会出现IllegalMonitorStateException异常
3. sleep和yield的区别
- sleep方法会给其他线程运行的机会,而不考虑其他线程的优先级,因此会给较低优先级的一个运行的机会;yield方法只会给相同优先级或者更高优先级的线程一个运行的机会
- 当线程执行了sleep方法后,将转到阻塞状态,参数millis指定睡眠时间;当线程执行yield方法后,将转到就绪状态
- sleep方法声明抛出InterruptedException异常;而yield方法没有声明抛出任何异常
- sleep方法比yield方法具有更好的可移植性
4. notify和notifyall区别
- notify和notifyall都是唤醒线程,从等待队列进入锁池队列
- notify随机唤醒等待队列中的一个线程,notifyall唤醒等待队列中的全部线程
参考