| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> Java知识库 -> java并发编程的艺术读书笔记3 -> 正文阅读 |
|
[Java知识库]java并发编程的艺术读书笔记3 |
concurrent包的实现. 上文中我们已经知道.java的CAS同时具有volatile读写的效果,Java线程间通信至少有以下四种方式. A线程写volatile变量.B线程读这个变量 A线程CAS更新一个变量,B线程CAS更新一个变量 A线程写volatile变量.B线程CAS更新这个变量 A线程CAS更新一个volatile变量,B线程读取这个变量 接下来是一段看起来很棒的话 Java的CAS会使用现代处理器上提供的高效原子指令 这些指令可以原子的对内存执行读 改 写操作,这是在多处理器中实现同步的关键(本质上说,能够支持原子操作的计算机是顺序计算图灵记的异步等价机器) 同时 volatile读写和CAS可以实现线程间通信,这些特性整合就形成了concurrent包实现的基石.其中一个通用的形式是 声明一个volatile变量 使用CAS原子条件更新实现线程间同步 配合以volatile读写和CAS具有的volatile读写内存语义实现线程间通信 AQS atomic? ? ? ? ?这些concurrent包中的基础类都是使用这种模式实现的.concurrent中的高层类是通过这些基础类实现的. Lock 同步器 阻塞队列 Executor 并发容器 AQS 非阻塞数据结构 原子变量类 volatile读写 CAS 3.6final内存含义 final的读写更像普通变量的访问? final重排序规则: 构造函数内对final域的初始化不得与构造对象的引用赋值给一个引用变量重排序 初次读包含final对象的引用和随后初次读这个final域重排序 写final域的规则: JMM禁止把构造函数对final域的写重排序到构造函数之外,编译器在构造函数中final域的写后,构造函数之前加入store store屏障。 读final域的规则: 线程中初次读包含final域对象的引用和初次读final域禁止重排序,JMM禁止处理器重排序这俩操作,编译器会在读final域之前插入load load屏障。 final域是引用类型会添加规则: 在构造函数中对一个final引用的对象的成员域的写入与随后在构造函数外把这个被构造对象引用赋值给变量不可重排序。 tips 要做到写final域规则确保的在引用变量为任意线程可见之前 该引用变量指向对象的final域已经被初始化了还需要保证构造函数内不能让这个被构造对象的引用为其他线程所见。 final语义在X86处理器中的实现,X86不会对写写操作重排序,因此storestore会被忽略掉,X86不会对存在间接依赖关系的操作重排序,在x86中,读final域需要的load load屏障也会被忽略,x86处理器中,final域的读写不插入任何内存屏障。 JMM设计理念 程序员希望内存模型尽量易于理解易于编程,需要一个强内存模型 处理器设计者希望内存模型尽量简单,这样可以做更多优化而不会被束缚 结果:找到两者的平衡点 分布式算法将偏序关系转全序关系?《Time,Clocks and the Ordering of Events in a Distributed System》值得读一下 JMM happens before原则: 程序顺序原则,一个线程中每个操作happensbefore线程的后续任意操作 监视器锁规则 对锁的解锁happens before对这个锁的加锁 volatile变量规则? volatile的写happensbefore 后续任意对它的读 传递性 start规则? a中startb a中的startb happensbeforeb中的任意操作 join规则 线程a执行操作b.join并成功返回 b中的任意操作happensbefore a从b.join成功返回 3.8双重检查锁定与延迟初始化 为了延迟初始化一些类以提高启动效率做了双重检查锁定。它是讲synchronized的经典案例之一。 双重检查锁定要注意volatile 没有volatile可能导致指令重排序讲未初始化的内存地址返回导致访问错误。 解决方案有两种 一种是防止初始化和返回引用地址重排序 ? ? ? ? ? ? ? ? ? ? ? ? 另一种是防止别的线程看到这个重排序 使用volatile关键字可以实现第一种 使用类初始化防止别的线程看到这个重排序 原理 jvm在类的初始化阶段 class被加载且被线程使用前 执行类的初始化。执行类的初始化期间 jvm获取一个锁 这个锁可以同步多个线程对同一个线程的初始化 使用内部类的方法初始化即可 初始化一个类包括执行这个类的静态初始化和初始化这个类中声明的静态字段,在首次发生 1 T是一个类而且它的实例被创建 2 T是一个类且T中声明的方法被调用 3 T中声明的一个静态字段被赋值 4 T中声明的一个静态字段被使用且不是一个常量字段 5 T是一个顶级类 且一个断言语句嵌套在T内部执行 Java多线程 多个线程同时间初始化一个类或接口 因此需要做细致的同步处理? 任意一个接口或类C JVM会做一个C-LC(对应的初始化锁)的映射 JVM在类初始化期间 获取这个初始化锁 过程: 1 在通过在Class对象上同步 控制类/接口的初始化 获取锁的线程会一直等待 知道能获取这个锁 2 线程a执行类的初始化 b在初始化锁对应的condition上等待 3 a设置state=initialized 唤醒condition中所有等待的线程 4 b结束类的初始化处理 5 线程c的初始化处理 (condition和state均为书中作者虚构 ) 总结:字段延迟初始化降低了初始化类或创建实例的开销 增加了被访问被延迟初始化的字段开销。多数情况正常初始化优于延迟初始化 如果确实需要对实例字段使用线程安全的延迟初始化 使用基于volatile的延迟初始化 如果需要对静态字段使用延迟初始化,使用基于类的初始化的方案。 3.9 Java内存模型综述 顺序一致性模型(理论参考模型) 放松写-读操作顺序 Total Store Ordering内存模型? 继续放松写-写操作 Partial Store Order内存模型 继续放松读-写 和读-读操作 产生Relaxed Memory Order和PowerPC内存模型 ps 读写放松是两个操作间不存在数据依赖性 大多数内存模型允许写读重排序 原因是他们都使用了写缓存区 可能导致写读重排序? JMM通过插入内存屏障屏蔽不同处理器内存模型差异为Java程序员提供一个一致的内存模型 单线程程序 编译器 runtime 处理器共同确保单线程程序与顺序一致性执行结果相同 正确同步的多线程程序 JMM通过限制编译器和处理器的重排序保证为程序员提供内存可见性保证 未正确同步多线程程序 JMM提供最小安全性保证 线程读取的值要么是之前某个线程写入的值要么是默认值 最小安全性保证与64位数据非原子性不矛盾? 最小安全性保证初始化之后才会被任意应用? 64位数据非原子性写发生在对象被多个线程使用过程中? 发生问题(处理器B看到A写了一半的值)时 虽然虽然B读取到一个被写了一半的值 但任然是A写入的,最小安全性保证要么是之前某个线程写入的值要么是默认值? |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 8:50:32- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |