Java内存模型学习之锁的内存语义 ReetrantLock的源码分析 Lock释放锁和获取锁的过程(公平锁和非公平锁)
锁的释放和锁的获取:
线程A释放一个锁,实质上是线程A向接下来将要获取这个锁的某个线程发出了(线程A对共享变量所做修改的)消息
线程B获取一个锁,实质上是线程B接收了之前某个线程发出的(在释放这个锁之前对共享变量所做修改的)消息
线程Ashifang释放锁,随后线程B获取这个锁,这个过程实质上是通过线程A通过主内存向线程B发送消息
package juc.lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Description
* @Author DJZ-WWS
* @Date 2019/5/13 14:13
*/
public class ReetrantTest {
int a=0;
ReentrantLock lock=new ReentrantLock();
public void writer(){
//获取锁
lock.lock();
try{
a++;
}finally {
//释放锁
lock.unlock();
}
}
public void read(){
lock.lock();//获取锁
try {
int i=a;
i++;
}finally {
lock.unlock();//释放锁
}
; }
}
ReentrantLock的实现依赖于java同步器AbstractQueueSynchronizer(习惯简称为AQS),它使用一个整型的volatile变量来维护同步状态,ReentrantLock分为公平锁,不公平锁,类中有如下源码
这个类图如下:
使用公平锁时,加锁方法LOck调用轨迹如下:
1.ReentrantLock:lock()
2.FairSync:lock()
2.AbstractQueueSynchronizer.acquire(int arg)。
ReentrantLock这个类中自带lock方法,
他的内部类使用了Syn内部类的对象调用了自身的lock方法,
上面的抽象方法有两个实现,一个是公平锁的一个是非公平锁的,这两个锁内部维护着一个lock方法,最终是通过这个最内部的lock方法里面的tryAcquire()方法实现加锁。
这个方法源码如下:
从源码中可以看出加锁方法首先读volatile变量state。
getState()方法如下
返回结果的state是使用volatile 修饰的,
以上便是lock实现对公平锁的调用流程
使用公平锁的时,解锁方法unlock()调用轨迹
1.ReentrantLock:unlock()
2.AbstractQueuedSynchronizer:release(int arg)。
3.Sync:tryRelease(int release)
最后一步真正实现释放锁
公平锁在释放锁的最后写volatile变量state,在获取锁的时候首先读取这个volatile变量。根据volatile的happen-before规则,释放锁的线程在写volatile之前可见的共享变量,在获取锁的线程读取同一个volatile变量后变得对获取锁的线程可见。
非公平锁的释放和公平锁完全一样。使用非公平锁加锁的lock的调用轨迹如下:
1>ReentrantLock:lock().
2>NonfairSync:lock();
3>AbstractQueuedSynchronizer:compareAndSetState(int expect,update
第三步的时候实现真正的加锁。
这是NonfairSync内部类的方法调用
,
它的底层调用了cas原子操作方法,对于cas前面一片文章已经介绍过,不再赘述
公平锁和非公平锁释放时最后都要写一个volatile变量state
公平锁和非公平锁释放时,最后都要写一个volatile变量state
公平锁获取时,首先回去读volatile变量
非公平锁获取时,首先会用CAS更新volatile变量,这个操作同时具有volatile读和volatile的内存语义。