最近看到一个专题讲解Java内存模型的,感觉干货满满,现记录如下:
在我之前的一篇博客 Java内存模型 中,对Java的内存做了一番简单的梳理。通过上述专题的阅读和学习,又有了新的认识和思考。
关于Java的锁
当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。这一操作和volatile变量的写操作相同功效
当线程获取锁时,JMM会把该线程对应的本地内存置为无效,这一操作和volatile变量的读操作相同共享
也即是文中总结的:锁释放与volatile写有相同的内存语义;锁获取与volatile读有相同的内存语义。
由此也自然联想到,Java锁的内部实现,其实就是通过volatile来实现的。
这里还涉及到公平锁,和非公平锁,这两者的区别是:
公平的锁上,线程按照他们发出请求的顺序获取锁.当一个线程请求非公平锁时,如果在发出请求的同时该锁变成可用状态,那么这个线程会跳过队列中所有的等待线程而获得锁。
关于Java的final
对于final域,编译器和处理器要遵守两个重排序规则:
- 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
- 初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。
在构造函数内部,不能让这个被构造对象的引用为其他线程可见,也就是对象引用不能在构造函数中“逸出”。
逸出还涉及到安全发布的概念(这里略去不细说)
重排序
重排序分为两大类:
- 编译器重排序
- 处理器重排序,该排序可细化为指令级并行的重排序和内存系统的重排序
内存屏障:对于处理器重排序,JMM的处理器重排序规则会要求java编译器在生成指令序列时,插入特定类型的内存屏障(memory barriers,intel称之为memory fence)指令,通过内存屏障指令来禁止特定类型的处理器重排序(不是所有的处理器重排序都要禁止)
内存模型
越是追求性能的处理器,内存模型设计的会越弱,模型由强变弱依次为:
顺序一致性内存模型
JMM
处理器内存模型
happens-before:如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在happens-before关系。这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之间.
参考资料