ReentrantLock是如何实现与synchronized一样的语义的?
一、synchronized可见性:
在Java内存模型中,synchronized规定,线程在加锁时,
获取锁→先清空工作内存→在主内存中拷贝最新变量的副本到工作内存→执行完代码→将更改后的共享变量的值刷新到主内存中→释放锁
而ReentrantLock是怎么做到在获取锁后重新从主内存获取最新的变量副本?
ReentrantLock是怎么做到在释放锁前将更改后的共享变量的值刷新到主内存中?
这两个问题是一样的,请继续看。
二、博客
解说1:
1、A线程写入volatile变量后,会把当前A线程工作内存中的写缓冲区的所有数据都刷回主内存 。并且通过MESI机制使这些变量在其他线程的缓存也失效,其他线程此时需要重新从主内存中读取拷贝这些变量的数据到工作内存中。
2、如果线程A读取了一个volatile
变量,则读取该volatile
变量时线程A可见的所有变量也将从主内存中重新读取。(读取了一个volatile
变量,则当前线程A的读缓存失效(清空),在此读取操作的后面读取其他非volatile但也是共享变量时,也都会重新读取主内存,而不是从工作内存中读取缓存数据(相当于读取了volatile变量,那么当前线程读缓存全部清空))
这段话参考自:http://tutorials.jenkov.com/java-concurrency/volatile.html#full-volatile-visibility-guarantee
三、知乎问答
链接:https://www.zhihu.com/question/41016480/answer/130906913
两个回答很不错:
1、
2、
四、lock前缀指令的作用:
把当前线程工作内存中的变更(可以理解为写缓冲区)都写到主内存中,并且使得其他cpu关于这部分数据的缓存行也都失效(MESI机制)。也就是说,unlock之后,其他线程使用使用这部分共享变量,由于MESI导致相关缓存行失效,那么读取这部分数据就会重新从主内存中读取数据。
五、ReentrantLock的lock和unlock方法
接下来可以说说ReentrantLock的lock和unlock方法了。
以非公平锁为例:
1、ReentrantLock.lock操作
源码截图:
采用cas操作state变量,即读取了state也会修改state。
所以lock之后使用的共享变量,都是会从主内存中读取最新的数据。
(当然,有修改state操作,那也会把调用lock方法之前的工作内存的写缓冲区的数据刷回主内存,并使其他cpu相关缓存行失效,不过这个意义不大,可以忽略,因为对于ReentrantLock来说,肯定是只需要保证lock与unlock之间的变量共享问题。)
2、ReentrantLock.unlock操作
源码,unlock最终调用的是内部类Sync的tryRelease方法:
unlock是设置了state的值,也就是写操作。
对volatile变量的写操作,会紧跟一个LOCK前缀指令,LOCK前缀的指令效果是把工作内存中的变更(可以理解为写缓冲区)都写到主内存中,并且使得其他cpu关于这部分数据的缓存行也都失效(MESI机制)。也就是说,unlock之后,其他线程使用使用这部分共享变量,会重新从主内存中读取数据。
和synchronized退出代码块释放锁时一样。保证释放锁是,刷回主内存。