JUC学习笔记——共享模型之管程在本系列内容中我们会对JUC做一个系统的学习,本片将会介绍JUC的管程部分
我们会分为以下几部分进行介绍:
- 共享问题
- 共享问题解决方案
- 线程安全分析
- Monitor
- synchronized锁
- Wait/notify
- 模式之保护性暂停
- 模式之生产者消费者
- park
- 线程状态转换详解
- 多锁操作
- 活跃性
- ReentrantLock
- 同步模式之顺序控制
共享问题概述我们首先来简单介绍一下贡献问题的产生原因:
- 操作系统目前只操纵一个CPU单位(单核CPU)
- 但是有两个线程都需要CPU来运行程序,所以操作系统采用时间片分配CPU
- 假设一个线程负责i++,一个线程负责i--,但我们需要注意共享数据的存放不是在线程中而是在内存里
- 假设一个线程取到数据,并进行i++操作之后,但并未将数据放入时,发生了上下文转换,这时另一个线程完成了i--操作
- 这时另一个线程的操作结果为0-1:-1,结果这个线程继续操作,将计算后的数据直接放入,结果变为了1,结果错误引发共享问题
// 针对counter,我们一个线程++,一个线程--各运行5000次static int counter = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 5000; i++) {counter++;}}, "t1");Thread t2 = new Thread(() -> {for (int i = 0; i < 5000; i++) {counter--;}}, "t2");t1.start();t2.start();t1.join();t2.join();log.debug("{}",counter);}// 但结果却不是0,经常为-5000~5000之间的数
我们可以从底层代码分析问题:/*i++底层代码*/ getstatic i // 获取静态变量i的值iconst_1// 准备常量1iadd// 自增putstatic i // 将修改后的值存入静态变量i/*i--底层代码*/ getstatic i // 获取静态变量i的值iconst_1// 准备常量1isub// 自减putstatic i // 将修改后的值存入静态变量i
我们会发现他们的底层代码并不是一步实现,而是多步操作一同实现在单线程下,按照正常顺序实现自然不会出错:
文章插图
但是如果是多线程,就会因为上下文切换的缘由导致部分步骤出现交杂(我们给出正数示例):
文章插图
临界区和竞态条件首先我们来简单介绍一下临界区:
- 一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区
- 例如我们上述共享问题中的i就是共享资源,而对i操作的i++和i--操作都可以被称为临界区
- 一个程序运行多个线程本身是没有问题的
- 多个线程读共享资源其实也没有问题
- 但是在多个线程对共享资源读写操作时发生指令交错,就会出现问题
- 多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件
共享问题解决方案总述我们的共享问题主要采用以下两种方案解决:
- 阻塞式的解决方案:synchronized,Lock
- 非阻塞式的解决方案:原子变量
- 俗称的【对象锁】,采用互斥的方式使目前至多只有一个线程能持有【对象锁】其它线程再想获取这个【对象锁】时就会阻塞住 。
经验总结扩展阅读
- Seata Server 1.5.2 源码学习
- 2022极端高温!机器学习如何预测森林火灾?? 万物AI
- 1.nginx学习
- 常用Python库整理
- 2023年谷雨学习运有所提升的星座学业突飞猛进
- notability怎么一键清空手写笔记 notability怎么清除所有笔
- 图学习参考资料 词向量word2vec
- 五 RK3568开发笔记:在虚拟机上使用SDK编译制作uboot、kernel和ubuntu镜像
- realme笔记本最新消息_realme笔记本即将发布
- JUC学习笔记——进程与线程