本帖最后由 李江 于 2013-10-8 08:18 编辑
锁释放和获取的内存语义当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。以上面的MonitorExample程序为例,A线程释放锁后,共享数据的状态示意图如下(图二):
当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须要从主内存中去读取共享变量。下面是锁获取的状态示意图(图三):
对比锁释放-获取的内存语义与volatile写-读的内存语义,可以看出:锁释放与volatile写有相同的内存语义;锁获取与volatile读有相同的内存语义。下面对锁释放和锁获取的内存语义做个总结: - 线程A释放一个锁,实质上是线程A向接下来将要获取这个锁的某个线程发出了(线程A对共享变量所做修改的)消息。
- 线程B获取一个锁,实质上是线程B接收了之前某个线程发出的(在释放这个锁之前对共享变量所做修改的)消息。
- 线程A释放锁,随后线程B获取这个锁,这个过程实质上是线程A通过主内存向线程B发送消息。
锁内存语义的实现本文将借助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中,调用lock()方法获取锁;调用unlock()方法释放锁。ReentrantLock的实现依赖于java同步器框架AbstractQueuedSynchronizer(本文简称之为AQS)。AQS使用一个整型的volatile变量(命名为state)来维护同步状态,马上我们会看到,这个volatile变量是ReentrantLock内存语义实现的关键。 下面是ReentrantLock的类图(仅画出与本文相关的部分)(图四):
|