文章目录
线程状态
- 新建:线程新建
- 就绪:已经触发start(),还未抢占到CPU资源
- 运行:线程运行中
- 等待:无限制等待,被唤醒才能进入就绪状态
- 超时等待:有时间的等待,等待时间已过,就如就绪状态
- 阻塞:线程阻塞,需要获取到锁才能进入就绪状态
- 终止:现在执行完毕或者手动关闭
线程状态流转图
新建->就绪
线程调用 thread.start() 就如就绪状态
就绪->运行
就绪线程抢占到CPU时间片会进入到运行状态
运行->就绪
-
系统
运行中的线程不一定一直都会是运行状态,没有获得时间片又会回到就绪状态。
参考:线程的调度与时间片
-
yield()
作用:放弃当前的CPU资源,让其他任务去抢占CUP资源(只会给相同优先级或者更高优先级的现场运行的机会)
运行->等待、就绪->等待
- Object.wait()
- Object.join()
- JUC Lock Condition 中的await()
- LockSupport.park()
唤醒机制具体详解参考:https://blog.csdn.net/weixin_42480780/article/details/115904832
等待->就绪
- Object.notify()、Object.notifyAll()
- JUC Lock Condition 中的 signal()
- LockSupport.unpark
运行->超时等待、就绪->超时等待
- Object.sleep(long)
- Object.wait(long)
- Object.join(long)
- LockSupport.parkNanos(long)
- LockSupport.parkUntil(long)
超时等待->就绪
- 超时等待时间已过
- Object.notify()、Object.notifyAll()
- JUC Lock Condition 中的 signal()
- LockSupport.unpark
运行->阻塞、就绪->阻塞
- 抢占锁资源
运行->终止、就绪->终止
非介入终止
- 线程执行完成
介入终止线程的方法
- Thread.stop():线程不安全,不推荐
- 设置中断标识为thread.interrpt()
线程的调度与时间片
由于CPU的计算频率非常高,每秒计算数十亿次,
因此可以将CPU的时间从毫秒的维度进行分段,
每一小段叫作一个CPU时间片。
目前操作系统中主流的线程调度方式是:基于CPU时间片方式进行线程调度
线程只有得到CPU时间片才能执行指令,处于执行状态,
没有得到时间片的线程处于就绪状态,等待系统分配下一个CPU时间片。
由于时间片非常短,在各个线程之间快速地切换,
因此表现出来的特征是很多个线程在“同时执行”或者“并发执行”。
线程的调度模型目前主要分为两种:分时调度模型和抢占式调度模型。
-
分时调度模型:系统平均分配CPU的时间片,所有线程轮流占用CPU,即在时间片调度的分配上所有线程“人人平等”。
-
抢占式调度模型:系统按照线程优先级分配CPU时间片。优先级高的线程优先分配CPU时间片,如果所有就绪线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些。
由于目前大部分操作系统都是使用`抢占式调度模型`进行线程调度, Java的线程管理和调度是委托给操作系统完成的,与之相对应, Java的线程调度也是使用抢占式调度模型,因此Java的线程都有优先级。
线程等待方法
Thread类
thread.join()
作用:当前线程等待调用join()方法的线程执行完再执行
底层原理:利用wait()方法实现
thread.sleep(long)
作用:让当前线程休眠一段时间
特点:任何地方都能使用,需要捕获InterruptedException异常
thread.yield()
作用:线程让出CPU时间片
Object类
wait()
作用:线程等待
特点:必须配合synchronized使用,唤醒必须配合Object中的notify()、notifyAll()方法
JUC-LOCK-Condition类
await()
作用:线程等待
特点:必须配合lock使用,唤醒必须配合JUC-LOCK-Condition中的signal()、signalAll()方法
LockSupport类
park()
作用:线程等待
特点:底层利用Unsafe类实现
线程等待方法的区别
thread.sleep(long) 与 object.wait() 与 object.wait(long) 的区别
-
使用限制
sleep(long)在线程的任何地方都能使用、 wait() 、wait(long) 需要配合synchronized使用 wait() 唤醒需要配合notify() 或者 notifyAll() 使用
-
不同的类
sleep(long) 在 Thread类中,wait()、wait(long)在Object类中 为什么这样设计? 因为sleep(long)是让当前线程休眠, 不涉及到对象类,也不需要获取对象的锁, 所以是线程类方法。wait()是让获得对象锁的线程实现等待, 前提是获取到对象的锁,所以是类的方法。
-
使用场景
sleep() 一般用户当前线程休眠,wait() 用于多线程之间的通信
-
释放锁
wait()会释放获取到的锁并允许其他线程获取锁并继续执行,而sleep() 不会
-
线程切换(CPU资源)
sleep()会让出CUP时间片,强制上下文切换
wait()会让出CPU时间片,但是不会强制上下文切换
thread.sleep(long) 与 thread.yield()的区别
相同点:
1. 暂停当前线程
2. 如果获取到锁资源,都不会释放锁
3. 都会让出CPU时间片资源
不同点:
1. sleep() 可以指定休眠时间,而yield则依赖CPU的时间片划分
2. yield不能被中断,而sleep则可以被中断(需要捕获中断异常)
3. sleep() 让出CPU时间片给其他线程运行的机会不会考虑优先级,而yield() 只会给相同优先级或者更高优先级的线程运行的机会。
4. sleep()方法声明抛出InterruptedException异常,yield没有