3.5 锁的内存语义
3.5.1 锁的释放–获取建立的happens-before关系
锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息。
3.5.2 锁的释放后和获取的内存语义
- 当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。
- 当线程获取锁时,JMM会把该线程对应的本地内存置为无效。
总结:锁释放与volatile写有相同的语义;锁获取与volatile读有相同的内存语义。
3.5.3 锁内存语义的实现
ReentrantLock的源代码
class ReentrantLockExample{
int a = 0;
ReentrantLock lock = new ReentrantLock();
public void writer(){
lock.lock(); //获取锁
try {
a++;
} finally {
lock.unlock(); //释放锁
}
}
public void reader(){
lock.lock(); //获取锁
try {
int i = a;
.....
} finally {
lock.unlock(); //释放锁
}
}
}
ReentrantLock的实现依赖于Java同步器框架AbstractQueuedSynchronizer(AQS)。AQS使用一个整型的volatile变量来维护同步状态。
ReentrantLock分为公平锁和非公平锁。
- 公平锁和非公平锁释放时,最后都要写一个volatile变量state。
- 公平锁获取时,首先会去读volatile变量。
- 非公平锁获取时,首先会用CAS更新volaitle变量,这个操作同时具有volatile读和volatile写的内存语义。
从本文对ReentrantLock的分析可以看出,锁释放-获取的内存语义的实现至少有以下两个方式:
- 利用volatile变量的写-读所具有的内存语义
- 利用CAS所附带的volatile读和volatile写的内存语义
3.5.4 concurrent包的实现
通用化的实现模式:
- 首先,声明共享变量为volatile
- 然后,使用CAS的原子条件更新来实现线程之间的同步
- 同时,配合以volatile的读、写和CAS所具有的volatile读和写的内存语义来实现线程之间的通信。