锁的作用是使关键部分相互排斥。本文解释了获得的另一个重要知识点 - 锁的语义。
锁定是Java并发编程中最重要的同步机制。在锁定区域外,释放锁的线程也可以发送到获得相同锁定的线程。
锁定释放和示例代码:
假设螺纹A执行writer()方法,然后threadB执行reader()方法。根据规范为hapens-beforefification,此过程中包含的hapens-be-be the hapens-可以将其分为3个类别。
图::显示了上述临时关系的图形表达。
总结:
释放锁之前,将对所有可见的共享变量可见线程A。螺纹B获得相同的锁后,将立即看到线程。
释放线程后,JMM将刷新与线程相对应的本地内存中的共享变量。线程释放后,锁定,共享数据状态的状态示意图如下:
共享数据的状态示意图
当获得线程时,与线程相对应的本地内存无效。结果,由监视器保护的关键区域代码必须从主内存中读取共享变量。
绘制锁定状态
可以看到锁定释放的比较 - 获取锁的内存语义以及通过挥发性编写的内存语义的比较:锁定版本具有与挥发性相同的内存语义;锁定获取与挥发性读取具有相同的内存语义。
总结:
分析重新进入的源代码,以分析锁定记忆语义的特定实现机制。
示例代码:
在重新进入中,调用锁定方法获取锁;调用unlock()方法以释放锁定。
Rectrantlock的实现取决于Java同步器框架Appractsqueuedsynchronind(aqs).AQS使用整数挥发性变量(状态)来维持同步状态。该挥发性变量是重新进入记忆语义的关键。
Reetrantlock的类图
Reentrantlock分为公平的锁和非式锁,首先分析公平锁。
使用公平锁定时,lock()的调用轨迹如下。
步骤4开始真正锁定,此方法的源代码是:
从上面的代码可以看出,锁定方法首先读取挥发性变量状态。
使用公平锁时,解锁方法解锁()调用轨迹如下:
步骤3开始释放锁。以下是此方法的源代码:
从上面的代码可以看出,发行锁定末尾的挥发性变量状态的释放。
总结公平锁:
根据挥发性的hapens-before规则,在获取锁定螺纹后,在挥发性变量之前可以看到相同挥发性变量之前可以看到释放锁的线程的共享变量。
现在分析非锁定锁:
请注意,非挂锁的释放和公平锁的释放是完全相同的,这是上面的源代码。因此,下面仅分析获得非挂锁的过程。
使用非锁定锁的使用轨迹,锁定方法锁定()如下:
步骤3开始真正锁定,此方法的源代码是:
此方法更新原子操作中的状态变量,即compareAndset()()的操作。JDK文档说明该方法如下:如果当前状态值等于预期值,则设置原子形式同步状态随着更新的价值。此操作具有挥发性读写的内存语义。
接下来,从编译器和处理器的角度分析,CAS如何通过挥发性和挥发性编写记忆语义。
编译器的角度:
如前所述,编译器在阅读挥发性后不会对任何内存操作进行排序,以挥发性地阅读。编译器将不会在挥发性和挥发性的前面对任何内存操作进行排序。这两种条件的组合意味着同时实现由挥发性和挥发性编写的记忆语义。编译器无法在CA和CAS前的任何内存操作上对其进行排序。
处理器的角度:
(我不知道C ++)此摘要需要JVM源代码,并且可能会汇总该错误。如果您需要理解这一点,请检查“ Java并发编程艺术” p。53。
sun.misc.unsafe中的比较swapint源代码如下:(不要理解不安全,请阅读上一篇文章)
这是本地方法。本地法律将在OpenJDK中调用C ++代码。假设它是当前的X86处理器。该程序将根据当前处理器的类型来确定非-CMPXCHG指令,以添加锁定前缀。
英特尔手册的说明要锁定前缀
上面2和3分的记忆屏障的效果足以实现挥发性和挥发性的记忆语义。
公平锁和非flat锁的摘要
获取锁的内存语义的实施摘要
由于Java的Cas具有挥发性和挥发性的记忆语义,因此Java线程之间的通信方法具有以下四种方式
Java的CAS使用现代处理器提供的高效率机器水平原子指令。这些原子指令是在内存执行模式处理中读取修改的作业。这是在多处理器中同步的关键。在同一时间,挥发性变量和CAS的读写可以实现线程之间的通信。这些特征是Java整个并发软件包的基石。
COLSURRENT软件包的概括实现模式
aqs(java.utilrent.locks.abstractqueedsynchronizer),非块数据结构和原子变量类(java.util.concurrent.Atomic packets)。同步软件包中的高级类别取决于这些基本类别。
查看并发软件包的实现图
并发包的实施图
本文总结了“ Java并行编程艺术”,下一篇文章总结了“最终记忆症状”,因此请继续关注。