JAVA内存模式-5-锁

从happens-before角度来看锁的释放-获取

跟volatile一样

锁的释放-获取的内存过程

跟volatile一样

如何实现锁的内存效果??

作者这里以ReentrantLock为案例进行讲解。

Java同步器框架AbstractQueuedSynchronizer,这里暂时简称为AQS。

做讲解ReentrantLock时作者分为公平锁、非公平锁,这里我再做详解:

ReentrantLock公平锁

获取锁:

释放锁:

公平锁lock时,首先读volatile变量、

公平锁unlock时,最后写volatile变量。

这里的意思是:A线程unlock时,volatile前面的StoreStore确保把之前修改的结果刷新到主内存,然后对其他线程内存可见,然后再通过StoreLoad结束。

A线程结束后,通知B线程。

B线程lock时,通过LoadLoad把A线程修改的结果放到CPU缓冲区里,然后再通过LoadStore指令执行lock后面的代码,直到运行到unlock为止,依次轮退。

ReentrantLock非公平锁

非公平锁的释放与公平锁完全一致,区别在于非公平锁的获取:

非公平锁的获取是通过sun.misc.Unsafe.compareAndSwapInt()方法获取,而该方法最后还是调用unsafe.cpp/atomic.cpp/atomic_windows
_x86.inline.hpp。(这里是针对windows操作系统x86处理器,不同操作系统不同处理器调用不同的hpp等。)

这里CPU如果是多核的话,会添加lock前缀,单核的就不添加(单核自己负责顺序一致性),而intel对lock前缀的解释是:

  1. 确保该前缀对内存的读-改-写操作原子执行。早期CPU处理lock执行期间会锁住内存总线导致其他CPU均无法通过内存总线访问主内存,由于代价太高,后来优化为:如果要访问的主内存区域在lock前缀指令执行期间已经在处理器内部的缓存中被锁定(即包含该内存区域的缓存行当前处于独占或者已修改状态)、并且该内存区域被完全包含在单个缓存行中,那么处理器将直接执行该指令。由于在指令执行期间该缓存行会一直被锁定,其他处理器无法读写该指令要访问的内存区域,因此能保证指令执行的原子性。这个操作过程叫做缓存锁定,通过这种方式来获取锁。这个操作也叫做CAS操作。
  2. 禁止该指令与之前、之后的读写指令重排序。
  3. 把写缓冲区中的所有数据刷新到主内存中。

这里的第2和3条具有跟volatile读写相同的内存效果。

个人理解:

公平锁调用JAVA的lock方法时,通过读volatile变量,间接使用LoadLoad、LoadStore来实现获取主内存数据、业务逻辑处理的功能。

非公平锁是通过利用CPU自己的lock前缀指令,而该lock前缀指令能完全实现volatile相同的内存效果。

concurrent包的实现依据

基于JAVA提供的CAS操作,外加volatile读写,因此JAVA线程之间的通信分为下面4种方式:

  1. A线程写volatile变量,随后B线程读该volatile变量。
  2. A线程写volatile变,随后B线程用CAS更新该volatile变量。
  3. A线程用CAS更新一个volatile变量,随后B线程用CAS更新该volatile变量。
  4. A线程用CAS更新一个volatile变量,随后B线程读该volatile变量。

JAVA的CAS最底层还是调用CPU提供的高效机器级别原子指令,这些原子级别的指令以原子方式对内存执行读-改-写操作,这是多处理器中实现同步的关键,再加上volatile内存特性,这就形成了concurrent包的基础,concurrent包有一个通用化的实现模式:

首先,声明共享变量为volatile;

其次,使用CAS的原子条件更新来实现线程之间的同步;

同时,配合volatile读写和CAS来实现线程之间的通信。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值